1102 lines
47 KiB
C++
1102 lines
47 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "RayTracing/RayTracingMaterialHitShaders.h"
|
|
|
|
#if RHI_RAYTRACING
|
|
|
|
#include "BasePassRendering.h"
|
|
#include "DeferredShadingRenderer.h"
|
|
#include "PipelineStateCache.h"
|
|
#include "RenderCore.h"
|
|
#include "ScenePrivate.h"
|
|
|
|
#include "Nanite/NaniteRayTracing.h"
|
|
|
|
#include "RayTracingDefinitions.h"
|
|
#include "RayTracingInstance.h"
|
|
#include "BuiltInRayTracingShaders.h"
|
|
#include "RaytracingOptions.h"
|
|
#include "RayTracingLighting.h"
|
|
#include "RayTracingDecals.h"
|
|
#include "PathTracing.h"
|
|
#include "RayTracing.h"
|
|
#include "RendererModule.h"
|
|
#include "ShaderPlatformCachedIniValue.h"
|
|
|
|
int32 GEnableRayTracingMaterials = 1;
|
|
static FAutoConsoleVariableRef CVarEnableRayTracingMaterials(
|
|
TEXT("r.RayTracing.EnableMaterials"),
|
|
GEnableRayTracingMaterials,
|
|
TEXT(" 0: bind default material shader that outputs placeholder data\n")
|
|
TEXT(" 1: bind real material shaders (default)\n"),
|
|
ECVF_RenderThreadSafe
|
|
);
|
|
|
|
int32 GCompileRayTracingMaterialCHS = 1;
|
|
static FAutoConsoleVariableRef CVarCompileRayTracingMaterialCHS(
|
|
TEXT("r.RayTracing.CompileMaterialCHS"),
|
|
GCompileRayTracingMaterialCHS,
|
|
TEXT(" 0: skip compilation of closest-hit shaders for materials (useful if only shadows or ambient occlusion effects are needed)\n")
|
|
TEXT(" 1: compile closest hit shaders for all ray tracing materials (default)\n"),
|
|
ECVF_ReadOnly | ECVF_RenderThreadSafe
|
|
);
|
|
|
|
int32 GCompileRayTracingMaterialAHS = 1;
|
|
static FAutoConsoleVariableRef CVarCompileRayTracingMaterialAHS(
|
|
TEXT("r.RayTracing.CompileMaterialAHS"),
|
|
GCompileRayTracingMaterialAHS,
|
|
TEXT(" 0: skip compilation of any-hit shaders for materials (useful if alpha masked or translucent materials are not needed)\n")
|
|
TEXT(" 1: compile any hit shaders for all ray tracing materials (default)\n"),
|
|
ECVF_ReadOnly | ECVF_RenderThreadSafe
|
|
);
|
|
|
|
static int32 GRayTracingNonBlockingPipelineCreation = 1;
|
|
static FAutoConsoleVariableRef CVarRayTracingNonBlockingPipelineCreation(
|
|
TEXT("r.RayTracing.NonBlockingPipelineCreation"),
|
|
GRayTracingNonBlockingPipelineCreation,
|
|
TEXT("Enable background ray tracing pipeline creation, without blocking RHI or Render thread.\n")
|
|
TEXT("Fallback opaque black material will be used for missing shaders meanwhile.\n")
|
|
TEXT(" 0: off (rendering will always use correct requested material)\n")
|
|
TEXT(" 1: on (default, non-blocking mode may sometimes use the fallback opaque black material outside of offline rendering scenarios)\n"),
|
|
ECVF_RenderThreadSafe);
|
|
|
|
// CVar defined in DeferredShadingRenderer.cpp
|
|
extern int32 GRayTracingUseTextureLod;
|
|
|
|
static bool IsSupportedVertexFactoryType(const FVertexFactoryType* VertexFactoryType)
|
|
{
|
|
return VertexFactoryType->SupportsRayTracing();
|
|
}
|
|
|
|
static bool AreRayTracingMaterialsCompiled(EShaderPlatform Platform)
|
|
{
|
|
static FShaderPlatformCachedIniValue<int32> CVarCompileMaterialCHS(TEXT("r.RayTracing.CompileMaterialCHS"));
|
|
static FShaderPlatformCachedIniValue<int32> CVarCompileMaterialAHS(TEXT("r.RayTracing.CompileMaterialAHS"));
|
|
|
|
return CVarCompileMaterialCHS.Get(Platform) || CVarCompileMaterialAHS.Get(Platform);
|
|
}
|
|
|
|
class FMaterialCHS : public FMeshMaterialShader, public FUniformLightMapPolicyShaderParametersType
|
|
{
|
|
DECLARE_INLINE_TYPE_LAYOUT_EXPLICIT_BASES(FMaterialCHS, NonVirtual, FMeshMaterialShader, FUniformLightMapPolicyShaderParametersType);
|
|
public:
|
|
FMaterialCHS(const FMeshMaterialShaderType::CompiledShaderInitializerType& Initializer)
|
|
: FMeshMaterialShader(Initializer)
|
|
{
|
|
PassUniformBuffer.Bind(Initializer.ParameterMap, FSceneTextureUniformParameters::FTypeInfo::GetStructMetadata()->GetShaderVariableName());
|
|
FUniformLightMapPolicyShaderParametersType::Bind(Initializer.ParameterMap);
|
|
}
|
|
|
|
FMaterialCHS() {}
|
|
|
|
void GetShaderBindings(
|
|
const FScene* Scene,
|
|
ERHIFeatureLevel::Type FeatureLevel,
|
|
const FPrimitiveSceneProxy* PrimitiveSceneProxy,
|
|
const FMaterialRenderProxy& MaterialRenderProxy,
|
|
const FMaterial& Material,
|
|
const TBasePassShaderElementData<FUniformLightMapPolicy>& ShaderElementData,
|
|
FMeshDrawSingleShaderBindings& ShaderBindings) const
|
|
{
|
|
FMeshMaterialShader::GetShaderBindings(Scene, FeatureLevel, PrimitiveSceneProxy, MaterialRenderProxy, Material, ShaderElementData, ShaderBindings);
|
|
|
|
FUniformLightMapPolicy::GetPixelShaderBindings(
|
|
PrimitiveSceneProxy,
|
|
ShaderElementData.LightMapPolicyElementData,
|
|
this,
|
|
ShaderBindings);
|
|
}
|
|
|
|
void GetElementShaderBindings(
|
|
const FShaderMapPointerTable& PointerTable,
|
|
const FScene* Scene,
|
|
const FSceneView* ViewIfDynamicMeshCommand,
|
|
const FVertexFactory* VertexFactory,
|
|
const EVertexInputStreamType InputStreamType,
|
|
ERHIFeatureLevel::Type FeatureLevel,
|
|
const FPrimitiveSceneProxy* PrimitiveSceneProxy,
|
|
const FMeshBatch& MeshBatch,
|
|
const FMeshBatchElement& BatchElement,
|
|
const TBasePassShaderElementData<FUniformLightMapPolicy>& ShaderElementData,
|
|
FMeshDrawSingleShaderBindings& ShaderBindings,
|
|
FVertexInputStreamArray& VertexStreams) const
|
|
{
|
|
FMeshMaterialShader::GetElementShaderBindings(PointerTable, Scene, ViewIfDynamicMeshCommand, VertexFactory, InputStreamType, FeatureLevel, PrimitiveSceneProxy, MeshBatch, BatchElement, ShaderElementData, ShaderBindings, VertexStreams);
|
|
}
|
|
};
|
|
|
|
static bool RTNeedsAnyHitShader(EBlendMode BlendMode)
|
|
{
|
|
switch (BlendMode)
|
|
{
|
|
case BLEND_Opaque: return false; // always hit
|
|
case BLEND_Masked: return true; // runs shader (NOTE: dithered masking gets turned into translucent for the path tracer)
|
|
case BLEND_Translucent: return true; // casts transparent (colored) shadows depending on the shading model setup (fake caustics or transparent shadows)
|
|
case BLEND_Additive: return false; // never hit for shadows, goes through the default shader instead, so no need to use AHS for primary rays
|
|
case BLEND_Modulate: return true; // casts colored shadows
|
|
case BLEND_AlphaComposite: return true;
|
|
case BLEND_AlphaHoldout: return false; // treat as opaque for shadows
|
|
case BLEND_TranslucentColoredTransmittance: return true; // NOTE: Substrate only
|
|
default: checkf(false, TEXT("Unhandled blend mode %d"), int(BlendMode)); return false;
|
|
}
|
|
}
|
|
|
|
template<typename LightMapPolicyType, bool UseAnyHitShader, bool UseIntersectionShader, bool UseRayConeTextureLod>
|
|
class TMaterialCHS : public FMaterialCHS
|
|
{
|
|
DECLARE_SHADER_TYPE(TMaterialCHS, MeshMaterial);
|
|
public:
|
|
|
|
TMaterialCHS(const FMeshMaterialShaderType::CompiledShaderInitializerType& Initializer)
|
|
: FMaterialCHS(Initializer)
|
|
{}
|
|
|
|
TMaterialCHS() {}
|
|
|
|
static bool ShouldCompilePermutation(const FMeshMaterialShaderPermutationParameters& Parameters)
|
|
{
|
|
if (!AreRayTracingMaterialsCompiled(Parameters.Platform))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (Parameters.MaterialParameters.MaterialDomain != MD_Surface)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
static FShaderPlatformCachedIniValue<int32> CVarCompileMaterialAHS(TEXT("r.RayTracing.CompileMaterialAHS"));
|
|
const bool bWantAnyHitShader = CVarCompileMaterialAHS.Get(Parameters.Platform) != 0 && RTNeedsAnyHitShader(Parameters.MaterialParameters.BlendMode);
|
|
const bool bSupportProceduralPrimitive = Parameters.VertexFactoryType->SupportsRayTracingProceduralPrimitive() && FDataDrivenShaderPlatformInfo::GetSupportsRayTracingProceduralPrimitive(Parameters.Platform);
|
|
|
|
return IsSupportedVertexFactoryType(Parameters.VertexFactoryType)
|
|
&& (bWantAnyHitShader == UseAnyHitShader)
|
|
&& LightMapPolicyType::ShouldCompilePermutation(Parameters)
|
|
&& ShouldCompileRayTracingShadersForProject(Parameters.Platform)
|
|
&& (bool)GRayTracingUseTextureLod == UseRayConeTextureLod
|
|
&& (UseIntersectionShader == bSupportProceduralPrimitive);
|
|
}
|
|
|
|
static void ModifyCompilationEnvironment(const FMaterialShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
|
|
{
|
|
// NOTE: Any CVars that are used in this function must be handled in ShaderMapAppendKeyString() to ensure shaders are recompiled when necessary.
|
|
static FShaderPlatformCachedIniValue<int32> CVarCompileMaterialCHS(TEXT("r.RayTracing.CompileMaterialCHS"));
|
|
|
|
OutEnvironment.SetDefine(TEXT("USE_MATERIAL_CLOSEST_HIT_SHADER"), CVarCompileMaterialCHS.Get(Parameters.Platform) ? 1 : 0);
|
|
OutEnvironment.SetDefine(TEXT("USE_MATERIAL_ANY_HIT_SHADER"), UseAnyHitShader ? 1 : 0);
|
|
OutEnvironment.SetDefine(TEXT("USE_MATERIAL_INTERSECTION_SHADER"), UseIntersectionShader ? 1 : 0);
|
|
OutEnvironment.SetDefine(TEXT("USE_RAYTRACED_TEXTURE_RAYCONE_LOD"), UseRayConeTextureLod ? 1 : 0);
|
|
OutEnvironment.SetDefine(TEXT("SCENE_TEXTURES_DISABLED"), 1);
|
|
LightMapPolicyType::ModifyCompilationEnvironment(Parameters, OutEnvironment);
|
|
FMeshMaterialShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
|
|
const bool VirtualTextureLightmaps = UseVirtualTextureLightmap(Parameters.Platform);
|
|
OutEnvironment.SetDefine(TEXT("LIGHTMAP_VT_ENABLED"), VirtualTextureLightmaps);
|
|
}
|
|
|
|
static bool ValidateCompiledResult(EShaderPlatform Platform, const FShaderParameterMap& ParameterMap, TArray<FString>& 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::RayTracingMaterial;
|
|
}
|
|
|
|
static const FShaderBindingLayout* GetShaderBindingLayout(const FShaderPermutationParameters& Parameters)
|
|
{
|
|
return RayTracing::GetShaderBindingLayout(Parameters.Platform);
|
|
}
|
|
};
|
|
|
|
class FTrivialMaterialCHS : public FMaterialCHS
|
|
{
|
|
DECLARE_SHADER_TYPE(FTrivialMaterialCHS, MeshMaterial);
|
|
public:
|
|
|
|
FTrivialMaterialCHS(const FMeshMaterialShaderType::CompiledShaderInitializerType& Initializer)
|
|
: FMaterialCHS(Initializer)
|
|
{}
|
|
|
|
FTrivialMaterialCHS() {}
|
|
|
|
static bool ShouldCompilePermutation(const FMeshMaterialShaderPermutationParameters& Parameters)
|
|
{
|
|
if (AreRayTracingMaterialsCompiled(Parameters.Platform))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return IsSupportedVertexFactoryType(Parameters.VertexFactoryType)
|
|
&& ShouldCompileRayTracingShadersForProject(Parameters.Platform);
|
|
}
|
|
|
|
static void ModifyCompilationEnvironment(const FMaterialShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
|
|
{
|
|
}
|
|
|
|
static bool ValidateCompiledResult(EShaderPlatform Platform, const FShaderParameterMap& ParameterMap, TArray<FString>& OutError)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
static ERayTracingPayloadType GetRayTracingPayloadType(const int32 PermutationId)
|
|
{
|
|
return ERayTracingPayloadType::RayTracingMaterial;
|
|
}
|
|
|
|
static const FShaderBindingLayout* GetShaderBindingLayout(const FShaderPermutationParameters& Parameters)
|
|
{
|
|
return RayTracing::GetShaderBindingLayout(Parameters.Platform);
|
|
}
|
|
};
|
|
|
|
IMPLEMENT_MATERIAL_SHADER_TYPE(, FTrivialMaterialCHS, TEXT("/Engine/Private/RayTracing/RayTracingMaterialDefaultHitShaders.usf"), TEXT("closesthit=OpaqueShadowCHS"), SF_RayHitGroup);
|
|
|
|
#define IMPLEMENT_MATERIALCHS_TYPE(LightMapPolicyType, LightMapPolicyName, AnyHitShaderName) \
|
|
typedef TMaterialCHS<LightMapPolicyType, false, false, false> TMaterialCHS##LightMapPolicyName; \
|
|
IMPLEMENT_MATERIAL_SHADER_TYPE(template<>, TMaterialCHS##LightMapPolicyName, TEXT("/Engine/Private/RayTracing/RayTracingMaterialHitShaders.usf"), TEXT("closesthit=MaterialCHS"), SF_RayHitGroup); \
|
|
typedef TMaterialCHS<LightMapPolicyType, true, false, false> TMaterialCHS##LightMapPolicyName##AnyHitShaderName; \
|
|
IMPLEMENT_MATERIAL_SHADER_TYPE(template<>, TMaterialCHS##LightMapPolicyName##AnyHitShaderName, TEXT("/Engine/Private/RayTracing/RayTracingMaterialHitShaders.usf"), TEXT("closesthit=MaterialCHS anyhit=MaterialAHS"), SF_RayHitGroup) \
|
|
typedef TMaterialCHS<LightMapPolicyType, false, false, true> TMaterialCHSLod##LightMapPolicyName; \
|
|
IMPLEMENT_MATERIAL_SHADER_TYPE(template<>, TMaterialCHSLod##LightMapPolicyName, TEXT("/Engine/Private/RayTracing/RayTracingMaterialHitShaders.usf"), TEXT("closesthit=MaterialCHS"), SF_RayHitGroup); \
|
|
typedef TMaterialCHS<LightMapPolicyType, true, false, true> TMaterialCHSLod##LightMapPolicyName##AnyHitShaderName; \
|
|
IMPLEMENT_MATERIAL_SHADER_TYPE(template<>, TMaterialCHSLod##LightMapPolicyName##AnyHitShaderName, TEXT("/Engine/Private/RayTracing/RayTracingMaterialHitShaders.usf"), TEXT("closesthit=MaterialCHS anyhit=MaterialAHS"), SF_RayHitGroup); \
|
|
typedef TMaterialCHS<LightMapPolicyType, false, true, false> TMaterialCHS_IS_##LightMapPolicyName; \
|
|
IMPLEMENT_MATERIAL_SHADER_TYPE(template<>, TMaterialCHS_IS_##LightMapPolicyName, TEXT("/Engine/Private/RayTracing/RayTracingMaterialHitShaders.usf"), TEXT("closesthit=MaterialCHS intersection=MaterialIS"), SF_RayHitGroup); \
|
|
typedef TMaterialCHS<LightMapPolicyType, true, true, false> TMaterialCHS_IS_##LightMapPolicyName##AnyHitShaderName; \
|
|
IMPLEMENT_MATERIAL_SHADER_TYPE(template<>, TMaterialCHS_IS_##LightMapPolicyName##AnyHitShaderName, TEXT("/Engine/Private/RayTracing/RayTracingMaterialHitShaders.usf"), TEXT("closesthit=MaterialCHS anyhit=MaterialAHS intersection=MaterialIS"), SF_RayHitGroup) \
|
|
typedef TMaterialCHS<LightMapPolicyType, false, true, true> TMaterialCHS_IS_Lod##LightMapPolicyName; \
|
|
IMPLEMENT_MATERIAL_SHADER_TYPE(template<>, TMaterialCHS_IS_Lod##LightMapPolicyName, TEXT("/Engine/Private/RayTracing/RayTracingMaterialHitShaders.usf"), TEXT("closesthit=MaterialCHS intersection=MaterialIS"), SF_RayHitGroup); \
|
|
typedef TMaterialCHS<LightMapPolicyType, true, true, true> TMaterialCHS_IS_Lod##LightMapPolicyName##AnyHitShaderName; \
|
|
IMPLEMENT_MATERIAL_SHADER_TYPE(template<>, TMaterialCHS_IS_Lod##LightMapPolicyName##AnyHitShaderName, TEXT("/Engine/Private/RayTracing/RayTracingMaterialHitShaders.usf"), TEXT("closesthit=MaterialCHS anyhit=MaterialAHS intersection=MaterialIS"), SF_RayHitGroup);
|
|
|
|
IMPLEMENT_MATERIALCHS_TYPE(TUniformLightMapPolicy<LMP_NO_LIGHTMAP>, FNoLightMapPolicy, FAnyHitShader);
|
|
IMPLEMENT_MATERIALCHS_TYPE(TUniformLightMapPolicy<LMP_PRECOMPUTED_IRRADIANCE_VOLUME_INDIRECT_LIGHTING>, FPrecomputedVolumetricLightmapLightingPolicy, FAnyHitShader);
|
|
IMPLEMENT_MATERIALCHS_TYPE(TUniformLightMapPolicy<LMP_LQ_LIGHTMAP>, TLightMapPolicyLQ, FAnyHitShader);
|
|
IMPLEMENT_MATERIALCHS_TYPE(TUniformLightMapPolicy<LMP_HQ_LIGHTMAP>, TLightMapPolicyHQ, FAnyHitShader);
|
|
IMPLEMENT_MATERIALCHS_TYPE(TUniformLightMapPolicy<LMP_DISTANCE_FIELD_SHADOWS_AND_HQ_LIGHTMAP>, TDistanceFieldShadowsAndLightMapPolicyHQ, FAnyHitShader);
|
|
|
|
IMPLEMENT_GLOBAL_SHADER(FHiddenMaterialHitGroup, "/Engine/Private/RayTracing/RayTracingMaterialDefaultHitShaders.usf", "closesthit=HiddenMaterialCHS anyhit=HiddenMaterialAHS", SF_RayHitGroup);
|
|
IMPLEMENT_GLOBAL_SHADER(FOpaqueShadowHitGroup, "/Engine/Private/RayTracing/RayTracingMaterialDefaultHitShaders.usf", "closesthit=OpaqueShadowCHS", SF_RayHitGroup);
|
|
IMPLEMENT_GLOBAL_SHADER(FDefaultCallableShader, "/Engine/Private/RayTracing/RayTracingMaterialDefaultHitShaders.usf", "DefaultCallableShader", SF_RayCallable);
|
|
|
|
// Select TextureLOD
|
|
template<typename LightMapPolicyType, bool bUseAnyHitShader, bool bUseIntersectionShader>
|
|
inline void GetMaterialHitShader_TextureLOD(FMaterialShaderTypes& ShaderTypes, bool bUseTextureLod)
|
|
{
|
|
if (bUseTextureLod)
|
|
{
|
|
ShaderTypes.AddShaderType<TMaterialCHS<LightMapPolicyType, bUseAnyHitShader, bUseIntersectionShader, true>>();
|
|
}
|
|
else
|
|
{
|
|
ShaderTypes.AddShaderType<TMaterialCHS<LightMapPolicyType, bUseAnyHitShader, bUseIntersectionShader, false>>();
|
|
}
|
|
}
|
|
|
|
// Select Intersection shader
|
|
template<typename LightMapPolicyType, bool bUseAnyHitShader>
|
|
inline void GetMaterialHitShader_Intersection_TextureLOD(FMaterialShaderTypes& ShaderTypes, bool bUseIntersectionShader, bool bUseTextureLod)
|
|
{
|
|
if (bUseIntersectionShader)
|
|
{
|
|
GetMaterialHitShader_TextureLOD<LightMapPolicyType, bUseAnyHitShader, true>(ShaderTypes, bUseTextureLod);
|
|
}
|
|
else
|
|
{
|
|
GetMaterialHitShader_TextureLOD<LightMapPolicyType, bUseAnyHitShader, false>(ShaderTypes, bUseTextureLod);
|
|
}
|
|
}
|
|
|
|
// Select AnyHit shader
|
|
template<typename LightMapPolicyType>
|
|
inline void GetMaterialHitShader_AnyHit_Intersection_TextureLOD(FMaterialShaderTypes& ShaderTypes, bool bUseAnyHitShader, bool bUseIntersectionShader, bool bUseTextureLod)
|
|
{
|
|
if (bUseAnyHitShader)
|
|
{
|
|
GetMaterialHitShader_Intersection_TextureLOD<LightMapPolicyType, true>(ShaderTypes, bUseIntersectionShader, bUseTextureLod);
|
|
}
|
|
else
|
|
{
|
|
GetMaterialHitShader_Intersection_TextureLOD<LightMapPolicyType, false>(ShaderTypes, bUseIntersectionShader, bUseTextureLod);
|
|
}
|
|
}
|
|
|
|
template<typename LightMapPolicyType>
|
|
static bool GetMaterialHitShader(const FMaterial& RESTRICT MaterialResource, const FVertexFactory* VertexFactory, bool UseTextureLod, EShaderPlatform Platform, TShaderRef<FMaterialCHS>& OutShader)
|
|
{
|
|
const bool bMaterialsCompiled = AreRayTracingMaterialsCompiled(Platform);
|
|
checkf(bMaterialsCompiled, TEXT("Material hit shaders are requested but they were not compiled for current platform [%s]"), *LexToString(Platform));
|
|
|
|
FMaterialShaderTypes ShaderTypes;
|
|
const FVertexFactoryType* VFType = VertexFactory->GetType();
|
|
const bool bUseIntersectionShader = VFType->HasFlags(EVertexFactoryFlags::SupportsRayTracingProceduralPrimitive) && FDataDrivenShaderPlatformInfo::GetSupportsRayTracingProceduralPrimitive(GMaxRHIShaderPlatform);
|
|
const bool UseAnyHitShader = (MaterialResource.IsMasked() || RTNeedsAnyHitShader(MaterialResource.GetBlendMode())) && GCompileRayTracingMaterialAHS;
|
|
|
|
GetMaterialHitShader_AnyHit_Intersection_TextureLOD<LightMapPolicyType>(ShaderTypes, UseAnyHitShader, bUseIntersectionShader, UseTextureLod);
|
|
|
|
FMaterialShaders Shaders;
|
|
if (!MaterialResource.TryGetShaders(ShaderTypes, VertexFactory->GetType(), Shaders))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Shaders.TryGetShader(SF_RayHitGroup, OutShader);
|
|
return true;
|
|
}
|
|
|
|
static bool GetRayTracingMeshProcessorShaders(
|
|
const FUniformLightMapPolicy& RESTRICT LightMapPolicy,
|
|
const FVertexFactory* VertexFactory,
|
|
const FMaterial& RESTRICT MaterialResource,
|
|
EShaderPlatform Platform,
|
|
TShaderRef<FMaterialCHS>& OutRayHitGroupShader)
|
|
{
|
|
check(GRHISupportsRayTracingShaders);
|
|
|
|
const bool bMaterialsCompiled = AreRayTracingMaterialsCompiled(Platform);
|
|
|
|
if (bMaterialsCompiled)
|
|
{
|
|
const bool bUseTextureLOD = bool(GRayTracingUseTextureLod);
|
|
|
|
switch (LightMapPolicy.GetIndirectPolicy())
|
|
{
|
|
case LMP_PRECOMPUTED_IRRADIANCE_VOLUME_INDIRECT_LIGHTING:
|
|
if (!GetMaterialHitShader<TUniformLightMapPolicy<LMP_PRECOMPUTED_IRRADIANCE_VOLUME_INDIRECT_LIGHTING>>(MaterialResource, VertexFactory, bUseTextureLOD, Platform, OutRayHitGroupShader))
|
|
{
|
|
return false;
|
|
}
|
|
break;
|
|
case LMP_LQ_LIGHTMAP:
|
|
if (!GetMaterialHitShader<TUniformLightMapPolicy<LMP_LQ_LIGHTMAP>>(MaterialResource, VertexFactory, bUseTextureLOD, Platform, OutRayHitGroupShader))
|
|
{
|
|
return false;
|
|
}
|
|
break;
|
|
case LMP_HQ_LIGHTMAP:
|
|
if (!GetMaterialHitShader<TUniformLightMapPolicy<LMP_HQ_LIGHTMAP>>(MaterialResource, VertexFactory, bUseTextureLOD, Platform, OutRayHitGroupShader))
|
|
{
|
|
return false;
|
|
}
|
|
break;
|
|
case LMP_DISTANCE_FIELD_SHADOWS_AND_HQ_LIGHTMAP:
|
|
if (!GetMaterialHitShader<TUniformLightMapPolicy<LMP_DISTANCE_FIELD_SHADOWS_AND_HQ_LIGHTMAP>>(MaterialResource, VertexFactory, bUseTextureLOD, Platform, OutRayHitGroupShader))
|
|
{
|
|
return false;
|
|
}
|
|
break;
|
|
case LMP_NO_LIGHTMAP:
|
|
if (!GetMaterialHitShader<TUniformLightMapPolicy<LMP_NO_LIGHTMAP>>(MaterialResource, VertexFactory, bUseTextureLOD, Platform, OutRayHitGroupShader))
|
|
{
|
|
return false;
|
|
}
|
|
break;
|
|
default:
|
|
check(false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FMaterialShaderTypes ShaderTypes;
|
|
ShaderTypes.AddShaderType<FTrivialMaterialCHS>();
|
|
|
|
FMaterialShaders Shaders;
|
|
if (!MaterialResource.TryGetShaders(ShaderTypes, VertexFactory->GetType(), Shaders))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Shaders.TryGetShader(SF_RayHitGroup, OutRayHitGroupShader);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
FRayTracingMeshProcessor::FRayTracingMeshProcessor(FRayTracingMeshCommandContext* InCommandContext, const FScene* InScene, const FSceneView* InViewIfDynamicMeshCommand, ERayTracingType InRayTracingType)
|
|
:
|
|
CommandContext(InCommandContext),
|
|
Scene(InScene),
|
|
ViewIfDynamicMeshCommand(InViewIfDynamicMeshCommand),
|
|
FeatureLevel(InScene ? InScene->GetFeatureLevel() : GMaxRHIFeatureLevel),
|
|
RayTracingType(InRayTracingType)
|
|
{
|
|
}
|
|
|
|
FRayTracingMeshProcessor::~FRayTracingMeshProcessor() = default;
|
|
|
|
bool FRayTracingMeshProcessor::Process(
|
|
const FMeshBatch& RESTRICT MeshBatch,
|
|
uint64 BatchElementMask,
|
|
const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy,
|
|
const FMaterialRenderProxy& RESTRICT MaterialRenderProxy,
|
|
const FMaterial& RESTRICT MaterialResource,
|
|
const FUniformLightMapPolicy& RESTRICT LightMapPolicy)
|
|
{
|
|
TShaderRef<FMaterialCHS> RayTracingShader;
|
|
if (GRHISupportsRayTracingShaders)
|
|
{
|
|
if (!GetRayTracingMeshProcessorShaders(LightMapPolicy, MeshBatch.VertexFactory, MaterialResource, Scene->GetShaderPlatform(), RayTracingShader))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
TBasePassShaderElementData<FUniformLightMapPolicy> ShaderElementData(MeshBatch.LCI);
|
|
ShaderElementData.InitializeMeshMaterialData(ViewIfDynamicMeshCommand, PrimitiveSceneProxy, MeshBatch, -1, true);
|
|
|
|
BuildRayTracingMeshCommands(
|
|
MeshBatch,
|
|
BatchElementMask,
|
|
PrimitiveSceneProxy,
|
|
MaterialRenderProxy,
|
|
MaterialResource,
|
|
RayTracingShader,
|
|
ShaderElementData);
|
|
|
|
return true;
|
|
}
|
|
|
|
void FRayTracingMeshProcessor::AddMeshBatch(const FMeshBatch& RESTRICT MeshBatch, uint64 BatchElementMask, const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy)
|
|
{
|
|
if (!MeshBatch.bUseForMaterial || !IsSupportedVertexFactoryType(MeshBatch.VertexFactory->GetType()))
|
|
{
|
|
return;
|
|
}
|
|
|
|
const FMaterialRenderProxy* FallbackMaterialRenderProxyPtr = MeshBatch.MaterialRenderProxy;
|
|
while (FallbackMaterialRenderProxyPtr)
|
|
{
|
|
const FMaterial* Material = FallbackMaterialRenderProxyPtr->GetMaterialNoFallback(FeatureLevel);
|
|
if (Material && Material->GetRenderingThreadShaderMap())
|
|
{
|
|
if (TryAddMeshBatch(MeshBatch, BatchElementMask, PrimitiveSceneProxy, -1, *FallbackMaterialRenderProxyPtr, *Material))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
FallbackMaterialRenderProxyPtr = FallbackMaterialRenderProxyPtr->GetFallback(FeatureLevel);
|
|
}
|
|
}
|
|
|
|
bool FRayTracingMeshProcessor::TryAddMeshBatch(
|
|
const FMeshBatch& RESTRICT MeshBatch,
|
|
uint64 BatchElementMask,
|
|
const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy,
|
|
int32 StaticMeshId,
|
|
const FMaterialRenderProxy& MaterialRenderProxy,
|
|
const FMaterial& Material
|
|
)
|
|
{
|
|
// Only draw opaque materials.
|
|
if ((!PrimitiveSceneProxy || PrimitiveSceneProxy->ShouldRenderInMainPass())
|
|
&& ShouldIncludeDomainInMeshPass(Material.GetMaterialDomain()))
|
|
{
|
|
if (RayTracingType == ERayTracingType::PathTracing ||
|
|
RayTracingType == ERayTracingType::LightMapTracing)
|
|
{
|
|
// Path Tracer has its own process call so that it can attach its own material permutation
|
|
return ProcessPathTracing(MeshBatch, BatchElementMask, PrimitiveSceneProxy, MaterialRenderProxy, Material);
|
|
}
|
|
|
|
// Check for a cached light-map.
|
|
const bool bIsLitMaterial = Material.GetShadingModels().IsLit();
|
|
const bool bAllowStaticLighting = IsStaticLightingAllowed();
|
|
|
|
const FLightMapInteraction LightMapInteraction = (bAllowStaticLighting && MeshBatch.LCI && bIsLitMaterial)
|
|
? MeshBatch.LCI->GetLightMapInteraction(FeatureLevel)
|
|
: FLightMapInteraction();
|
|
|
|
// force LQ lightmaps based on system settings
|
|
const bool bPlatformAllowsHighQualityLightMaps = AllowHighQualityLightmaps(FeatureLevel);
|
|
const bool bAllowHighQualityLightMaps = bPlatformAllowsHighQualityLightMaps && LightMapInteraction.AllowsHighQualityLightmaps();
|
|
|
|
const bool bAllowIndirectLightingCache = Scene && Scene->PrecomputedLightVolumes.Num() > 0;
|
|
const bool bUseVolumetricLightmap = Scene && Scene->VolumetricLightmapSceneData.HasData();
|
|
|
|
{
|
|
static const auto CVarSupportLowQualityLightmap = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.SupportLowQualityLightmaps"));
|
|
const bool bAllowLowQualityLightMaps = (!CVarSupportLowQualityLightmap) || (CVarSupportLowQualityLightmap->GetValueOnAnyThread() != 0);
|
|
|
|
switch (LightMapInteraction.GetType())
|
|
{
|
|
case LMIT_Texture:
|
|
if (bAllowHighQualityLightMaps)
|
|
{
|
|
const FShadowMapInteraction ShadowMapInteraction = (bAllowStaticLighting && MeshBatch.LCI && bIsLitMaterial)
|
|
? MeshBatch.LCI->GetShadowMapInteraction(FeatureLevel)
|
|
: FShadowMapInteraction();
|
|
|
|
if (ShadowMapInteraction.GetType() == SMIT_Texture)
|
|
{
|
|
return Process(
|
|
MeshBatch,
|
|
BatchElementMask,
|
|
PrimitiveSceneProxy,
|
|
MaterialRenderProxy,
|
|
Material,
|
|
FUniformLightMapPolicy(LMP_DISTANCE_FIELD_SHADOWS_AND_HQ_LIGHTMAP));
|
|
}
|
|
else
|
|
{
|
|
return Process(
|
|
MeshBatch,
|
|
BatchElementMask,
|
|
PrimitiveSceneProxy,
|
|
MaterialRenderProxy,
|
|
Material,
|
|
FUniformLightMapPolicy(LMP_HQ_LIGHTMAP));
|
|
}
|
|
}
|
|
else if (bAllowLowQualityLightMaps)
|
|
{
|
|
return Process(
|
|
MeshBatch,
|
|
BatchElementMask,
|
|
PrimitiveSceneProxy,
|
|
MaterialRenderProxy,
|
|
Material,
|
|
FUniformLightMapPolicy(LMP_LQ_LIGHTMAP));
|
|
}
|
|
else
|
|
{
|
|
return Process(
|
|
MeshBatch,
|
|
BatchElementMask,
|
|
PrimitiveSceneProxy,
|
|
MaterialRenderProxy,
|
|
Material,
|
|
FUniformLightMapPolicy(LMP_NO_LIGHTMAP));
|
|
}
|
|
default:
|
|
if (bIsLitMaterial
|
|
&& bAllowStaticLighting
|
|
&& Scene
|
|
&& Scene->VolumetricLightmapSceneData.HasData()
|
|
&& PrimitiveSceneProxy
|
|
&& (PrimitiveSceneProxy->IsMovable()
|
|
|| PrimitiveSceneProxy->NeedsUnbuiltPreviewLighting()
|
|
|| PrimitiveSceneProxy->GetLightmapType() == ELightmapType::ForceVolumetric))
|
|
{
|
|
return Process(
|
|
MeshBatch,
|
|
BatchElementMask,
|
|
PrimitiveSceneProxy,
|
|
MaterialRenderProxy,
|
|
Material,
|
|
FUniformLightMapPolicy(LMP_PRECOMPUTED_IRRADIANCE_VOLUME_INDIRECT_LIGHTING));
|
|
}
|
|
else
|
|
{
|
|
return Process(
|
|
MeshBatch,
|
|
BatchElementMask,
|
|
PrimitiveSceneProxy,
|
|
MaterialRenderProxy,
|
|
Material,
|
|
FUniformLightMapPolicy(LMP_NO_LIGHTMAP));
|
|
}
|
|
};
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool IsCompatibleFallbackPipelineSignature(FRayTracingPipelineStateSignature& B, FRayTracingPipelineStateSignature& A)
|
|
{
|
|
// Compare everything except hit group table
|
|
return A.MaxPayloadSizeInBytes == B.MaxPayloadSizeInBytes
|
|
&& A.GetRayGenHash() == B.GetRayGenHash()
|
|
&& A.GetRayMissHash() == B.GetRayMissHash()
|
|
&& A.GetCallableHash() == B.GetCallableHash();
|
|
}
|
|
|
|
static bool PipelineContainsHitShaders(FRayTracingPipelineState* Pipeline, const TArrayView<FRHIRayTracingShader*>& Shaders)
|
|
{
|
|
for (FRHIRayTracingShader* Shader : Shaders)
|
|
{
|
|
int32 Index = FindRayTracingHitGroupIndex(Pipeline, Shader, false);
|
|
if (Index == INDEX_NONE)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
FRHIRayTracingShader* GetRayTracingDefaultMissShader(const FGlobalShaderMap* ShaderMap)
|
|
{
|
|
return ShaderMap->GetShader<FPackedMaterialClosestHitPayloadMS>().GetRayTracingShader();
|
|
}
|
|
|
|
FRHIRayTracingShader* GetRayTracingDefaultOpaqueShader(const FGlobalShaderMap* ShaderMap)
|
|
{
|
|
return ShaderMap->GetShader<FOpaqueShadowHitGroup>().GetRayTracingShader();
|
|
}
|
|
|
|
FRHIRayTracingShader* GetRayTracingDefaultHiddenShader(const FGlobalShaderMap* ShaderMap)
|
|
{
|
|
return ShaderMap->GetShader<FHiddenMaterialHitGroup>().GetRayTracingShader();
|
|
}
|
|
|
|
void FDeferredShadingSceneRenderer::CreateMaterialRayTracingMaterialPipeline(
|
|
FRDGBuilder& GraphBuilder,
|
|
const TArrayView<FRHIRayTracingShader*>& RayGenShaderTable,
|
|
uint32& OutMaxLocalBindingDataSize,
|
|
bool& bOutIsUsingFallbackRTPSO)
|
|
{
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(FDeferredShadingSceneRenderer::CreateRayTracingMaterialPipeline);
|
|
SCOPE_CYCLE_COUNTER(STAT_CreateRayTracingPipeline);
|
|
|
|
FGlobalShaderMap* ShaderMap = GetGlobalShaderMap(ShaderPlatform);
|
|
|
|
FRHICommandList& RHICmdList = GraphBuilder.RHICmdList;
|
|
|
|
const bool bIsPathTracing = ViewFamily.EngineShowFlags.PathTracing;
|
|
const bool bSupportMeshDecals = bIsPathTracing;
|
|
|
|
ERayTracingPayloadType PayloadType = bIsPathTracing
|
|
? (ERayTracingPayloadType::PathTracingMaterial | ERayTracingPayloadType::Decals)
|
|
: ERayTracingPayloadType::RayTracingMaterial;
|
|
|
|
FRayTracingPipelineStateInitializer Initializer;
|
|
Initializer.MaxPayloadSizeInBytes = GetRayTracingPayloadTypeMaxSize(PayloadType);
|
|
|
|
const FShaderBindingLayout* ShaderBindingLayout = RayTracing::GetShaderBindingLayout(ShaderPlatform);
|
|
if (ShaderBindingLayout)
|
|
{
|
|
Initializer.ShaderBindingLayout = &ShaderBindingLayout->RHILayout;
|
|
}
|
|
|
|
FRHIRayTracingShader* DefaultMissShader = bIsPathTracing ? GetPathTracingDefaultMissShader(ShaderMap) : GetRayTracingDefaultMissShader(ShaderMap);
|
|
|
|
TArray<FRHIRayTracingShader*> RayTracingMissShaderLibrary;
|
|
FShaderMapResource::GetRayTracingMissShaderLibrary(ShaderPlatform, RayTracingMissShaderLibrary, DefaultMissShader);
|
|
|
|
// make sure we have at least one miss shader present
|
|
check(RayTracingMissShaderLibrary.Num() > 0);
|
|
|
|
Initializer.SetMissShaderTable(RayTracingMissShaderLibrary);
|
|
|
|
Initializer.SetRayGenShaderTable(RayGenShaderTable);
|
|
|
|
const bool bMaterialsCompiled = AreRayTracingMaterialsCompiled(ShaderPlatform);
|
|
const bool bEnableMaterials = bMaterialsCompiled && GEnableRayTracingMaterials != 0;
|
|
static auto CVarEnableShadowMaterials = IConsoleManager::Get().FindConsoleVariable(TEXT("r.RayTracing.Shadows.EnableMaterials"));
|
|
const bool bEnableShadowMaterials = bMaterialsCompiled && (CVarEnableShadowMaterials ? CVarEnableShadowMaterials->GetInt() != 0 : true);
|
|
|
|
FRHIRayTracingShader* OpaqueShadowShader = bIsPathTracing ? GetPathTracingDefaultOpaqueHitShader(ShaderMap) : GetRayTracingDefaultOpaqueShader(ShaderMap);
|
|
FRHIRayTracingShader* HiddenMaterialShader = bIsPathTracing ? GetPathTracingDefaultHiddenHitShader(ShaderMap) : GetRayTracingDefaultHiddenShader(ShaderMap);
|
|
|
|
FRHIRayTracingShader* OpaqueMeshDecalHitShader = bSupportMeshDecals ? GetDefaultOpaqueMeshDecalHitShader(ShaderMap) : nullptr;
|
|
FRHIRayTracingShader* HiddenMeshDecalHitShader = bSupportMeshDecals ? GetDefaultHiddenMeshDecalHitShader(ShaderMap) : nullptr;
|
|
|
|
TArray<FRHIRayTracingShader*> RayTracingHitGroupLibrary;
|
|
if (bEnableMaterials)
|
|
{
|
|
FShaderMapResource::GetRayTracingHitGroupLibrary(ShaderPlatform, RayTracingHitGroupLibrary, OpaqueShadowShader);
|
|
|
|
if (bSupportMeshDecals)
|
|
{
|
|
FShaderMapResource::GetRayTracingHitGroupLibrary(ShaderPlatform, RayTracingHitGroupLibrary, OpaqueMeshDecalHitShader);
|
|
}
|
|
}
|
|
|
|
FRHIRayTracingShader* RequiredHitShaders[] = { OpaqueShadowShader, HiddenMaterialShader };
|
|
FRHIRayTracingShader* RequiredHitDecalShaders[] = { OpaqueMeshDecalHitShader, HiddenMeshDecalHitShader };
|
|
|
|
RayTracingHitGroupLibrary.Append(RequiredHitShaders);
|
|
if (bSupportMeshDecals)
|
|
{
|
|
RayTracingHitGroupLibrary.Append(RequiredHitDecalShaders);
|
|
}
|
|
|
|
Initializer.SetHitGroupTable(RayTracingHitGroupLibrary);
|
|
|
|
// For now, only path tracing uses callable shaders (for decals). This is only enabled if the current platform supports callable shaders.
|
|
const bool bCallableShadersRequired = bIsPathTracing && RHISupportsRayTracingCallableShaders(ShaderPlatform);
|
|
TArray<FRHIRayTracingShader*> RayTracingCallableShaderLibrary;
|
|
FRHIRayTracingShader* DefaultCallableShader = nullptr;
|
|
|
|
if (bCallableShadersRequired)
|
|
{
|
|
DefaultCallableShader = ShaderMap->GetShader<FDefaultCallableShader>().GetRayTracingShader();
|
|
check(DefaultCallableShader != nullptr);
|
|
|
|
if (bEnableMaterials)
|
|
{
|
|
FShaderMapResource::GetRayTracingCallableShaderLibrary(ShaderPlatform, RayTracingCallableShaderLibrary, DefaultCallableShader);
|
|
}
|
|
else
|
|
{
|
|
RayTracingCallableShaderLibrary.Add(DefaultCallableShader);
|
|
}
|
|
|
|
Initializer.SetCallableTable(RayTracingCallableShaderLibrary);
|
|
}
|
|
|
|
bool bIsOfflineRender = false;
|
|
|
|
for(const FViewInfo& View : Views)
|
|
{
|
|
if (View.bIsOfflineRender)
|
|
{
|
|
bIsOfflineRender = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
const bool bAllowNonBlockingPipelineCreation = GRayTracingNonBlockingPipelineCreation && !bIsOfflineRender;
|
|
FRayTracingPipelineState* FallbackPipelineState = bAllowNonBlockingPipelineCreation
|
|
? PipelineStateCache::GetRayTracingPipelineState(Scene->LastRayTracingMaterialPipelineSignature)
|
|
: nullptr;
|
|
|
|
ERayTracingPipelineCacheFlags PipelineCacheFlags = ERayTracingPipelineCacheFlags::Default;
|
|
const bool bCompatiblePipelineSignatures = FallbackPipelineState ? IsCompatibleFallbackPipelineSignature(Scene->LastRayTracingMaterialPipelineSignature, Initializer) : false;
|
|
if (FallbackPipelineState
|
|
&& bCompatiblePipelineSignatures
|
|
&& PipelineContainsHitShaders(FallbackPipelineState, RequiredHitShaders)
|
|
&& (!bSupportMeshDecals || PipelineContainsHitShaders(FallbackPipelineState, RequiredHitDecalShaders))
|
|
&& FindRayTracingMissShaderIndex(FallbackPipelineState, DefaultMissShader, false) != INDEX_NONE
|
|
&& (!bCallableShadersRequired || FindRayTracingCallableShaderIndex(FallbackPipelineState, DefaultCallableShader, false) != INDEX_NONE))
|
|
{
|
|
PipelineCacheFlags |= ERayTracingPipelineCacheFlags::NonBlocking;
|
|
}
|
|
|
|
FRayTracingPipelineState* PipelineState = PipelineStateCache::GetAndOrCreateRayTracingPipelineState(RHICmdList, Initializer, PipelineCacheFlags);
|
|
|
|
if (PipelineState)
|
|
{
|
|
// Save the current pipeline to be used as fallback in future frames
|
|
Scene->LastRayTracingMaterialPipelineSignature = static_cast<FRayTracingPipelineStateSignature&>(Initializer);
|
|
}
|
|
else
|
|
{
|
|
// If pipeline was not found in cache, use the fallback from previous frame
|
|
check(FallbackPipelineState);
|
|
PipelineState = FallbackPipelineState;
|
|
bOutIsUsingFallbackRTPSO = true;
|
|
UE_LOG(LogRenderer, Log, TEXT("Using fallback RTPSO"));
|
|
}
|
|
|
|
// Retrieve the binding data size from the actual used RTPSO because the requested RTPSO could still be non blocking async compiling
|
|
// and then we are using the RTPSO from the previous frame
|
|
OutMaxLocalBindingDataSize = FMath::Max(OutMaxLocalBindingDataSize, GetRHIRayTracingPipelineStateMaxLocalBindingDataSize(PipelineState));
|
|
|
|
if (FallbackPipelineState != nullptr && PipelineState != FallbackPipelineState && bIsPathTracing && !bIsOfflineRender)
|
|
{
|
|
// When using path tracing, a change in pipeline state compared to the previous frame means some new materials got added to the RTPSO
|
|
// and we should restart sample accumulation.
|
|
// Only do this if the pipeline signatures are compatible, otherwise we might be toggling between Lit and PathTraced views and don't want to invalidate the state
|
|
if (bCompatiblePipelineSignatures)
|
|
{
|
|
Scene->InvalidatePathTracedOutput();
|
|
}
|
|
}
|
|
|
|
check(PipelineState);
|
|
|
|
// Send RTPSO to all views since they all share the same one
|
|
EnumerateLinkedViews([PipelineState](FViewInfo& View)
|
|
{
|
|
if (View.bHasAnyRayTracingPass)
|
|
{
|
|
View.MaterialRayTracingData.PipelineState = PipelineState;
|
|
}
|
|
return true;
|
|
});
|
|
}
|
|
|
|
void FDeferredShadingSceneRenderer::SetupMaterialRayTracingHitGroupBindings(FRDGBuilder& GraphBuilder, FViewInfo& View)
|
|
{
|
|
FRayTracingPipelineState* PipelineState = View.MaterialRayTracingData.PipelineState;
|
|
|
|
const bool bIsPathTracing = ViewFamily.EngineShowFlags.PathTracing;
|
|
const bool bSupportMeshDecals = bIsPathTracing;
|
|
const bool bMaterialsCompiled = AreRayTracingMaterialsCompiled(View.GetShaderPlatform());
|
|
const bool bEnableMaterials = bMaterialsCompiled && GEnableRayTracingMaterials != 0;
|
|
static auto CVarEnableShadowMaterials = IConsoleManager::Get().FindConsoleVariable(TEXT("r.RayTracing.Shadows.EnableMaterials"));
|
|
const bool bEnableShadowMaterials = bMaterialsCompiled && (CVarEnableShadowMaterials ? CVarEnableShadowMaterials->GetInt() != 0 : true);
|
|
|
|
FRHIRayTracingShader* OpaqueShadowShader = bIsPathTracing ? GetPathTracingDefaultOpaqueHitShader(View.ShaderMap) : GetRayTracingDefaultOpaqueShader(View.ShaderMap);
|
|
FRHIRayTracingShader* HiddenMaterialShader = bIsPathTracing ? GetPathTracingDefaultHiddenHitShader(View.ShaderMap) : GetRayTracingDefaultHiddenShader(View.ShaderMap);
|
|
|
|
const int32 OpaqueShadowMaterialIndex = FindRayTracingHitGroupIndex(PipelineState, OpaqueShadowShader, true);
|
|
const int32 HiddenMaterialIndex = FindRayTracingHitGroupIndex(PipelineState, HiddenMaterialShader, true);
|
|
|
|
const int32 OpaqueMeshDecalHitGroupIndex = bSupportMeshDecals ? FindRayTracingHitGroupIndex(PipelineState, GetDefaultOpaqueMeshDecalHitShader(View.ShaderMap), true) : INDEX_NONE;
|
|
const int32 HiddenMeshDecalHitGroupIndex = bSupportMeshDecals ? FindRayTracingHitGroupIndex(PipelineState, GetDefaultHiddenMeshDecalHitShader(View.ShaderMap), true) : INDEX_NONE;
|
|
|
|
// Scene UB is only needed when shader binding layout is not used because then it's bound via the global bindings
|
|
// Should ideally be lazy fetched during binding if needed
|
|
FRHIUniformBuffer* SceneUB = GetSceneUniforms().GetBufferRHI(GraphBuilder);
|
|
FRHIUniformBuffer* NaniteRayTracingUB = Nanite::GRayTracingManager.GetUniformBufferRHI(GraphBuilder);
|
|
|
|
// material hit groups
|
|
AddRayTracingLocalShaderBindingWriterTasks(GraphBuilder, View.DirtyPersistentRayTracingShaderBindings, View.MaterialRayTracingData.MaterialBindings,
|
|
[&View, PipelineState, OpaqueShadowMaterialIndex, HiddenMaterialIndex, OpaqueMeshDecalHitGroupIndex, HiddenMeshDecalHitGroupIndex, SceneUB, NaniteRayTracingUB, bIsPathTracing, bSupportMeshDecals,
|
|
bEnableMaterials, bEnableShadowMaterials, &RayTracingSBT = Scene->RayTracingSBT](const FRayTracingShaderBindingData& DirtyShaderBinding, FRayTracingLocalShaderBindingWriter* BindingWriter)
|
|
{
|
|
const FRayTracingMeshCommand& MeshCommand = *DirtyShaderBinding.RayTracingMeshCommand;
|
|
|
|
const bool bIsMeshDecalShader = MeshCommand.MaterialShader->RayTracingPayloadType == (uint32)ERayTracingPayloadType::Decals;
|
|
|
|
// TODO: Following check is disabled since FRayTracingMeshProcessor non-path-tracing code paths still don't assign the appropriate shader to decal mesh commands.
|
|
// We could also potentially use regular materials to approximate decals in ray tracing in some situations.
|
|
// check(bIsMeshDecalShader == MeshCommand.bDecal);
|
|
|
|
// Force the same shader to be used on all geometry unless materials are enabled
|
|
int32 HitGroupIndex;
|
|
|
|
if (bIsMeshDecalShader)
|
|
{
|
|
checkf(bSupportMeshDecals && MeshCommand.bDecal, TEXT("Unexpected ray tracing mesh command using Mesh Decal payload. Fix logic adding the command or update bSupportMeshDecals as appropriate."));
|
|
HitGroupIndex = DirtyShaderBinding.bHidden ? HiddenMeshDecalHitGroupIndex : OpaqueMeshDecalHitGroupIndex;
|
|
}
|
|
else
|
|
{
|
|
checkf((!bIsPathTracing && MeshCommand.MaterialShader->RayTracingPayloadType == (uint32)ERayTracingPayloadType::RayTracingMaterial)
|
|
|| (bIsPathTracing && MeshCommand.MaterialShader->RayTracingPayloadType == (uint32)ERayTracingPayloadType::PathTracingMaterial),
|
|
TEXT("Incorrectly using RayTracingMaterial when path tracer is enabled or vice-versa."));
|
|
HitGroupIndex = DirtyShaderBinding.bHidden ? HiddenMaterialIndex : OpaqueShadowMaterialIndex;
|
|
}
|
|
|
|
if (bEnableMaterials && !DirtyShaderBinding.bHidden)
|
|
{
|
|
const int32 FoundIndex = FindRayTracingHitGroupIndex(PipelineState, MeshCommand.MaterialShader, false);
|
|
if (FoundIndex != INDEX_NONE)
|
|
{
|
|
HitGroupIndex = FoundIndex;
|
|
}
|
|
else if (RayTracingSBT.IsPersistent())
|
|
{
|
|
check(DirtyShaderBinding.BindingType == ERayTracingLocalShaderBindingType::Transient);
|
|
check(RayTracingSBT.IsDirty(DirtyShaderBinding.SBTRecordIndex));
|
|
}
|
|
}
|
|
|
|
uint32 BaseRecordIndex = DirtyShaderBinding.SBTRecordIndex;
|
|
|
|
// Bind primary material shader
|
|
|
|
{
|
|
MeshCommand.SetRayTracingShaderBindingsForHitGroup(BindingWriter,
|
|
View.ViewUniformBuffer,
|
|
SceneUB,
|
|
NaniteRayTracingUB,
|
|
BaseRecordIndex + RAY_TRACING_SHADER_SLOT_MATERIAL,
|
|
DirtyShaderBinding.RayTracingGeometry,
|
|
MeshCommand.GeometrySegmentIndex,
|
|
HitGroupIndex,
|
|
DirtyShaderBinding.BindingType);
|
|
}
|
|
|
|
// Bind shadow shader
|
|
if (bIsMeshDecalShader)
|
|
{
|
|
// mesh decals do not use the shadow slot, so do minimal work
|
|
FRayTracingLocalShaderBindings& Binding = BindingWriter->AddWithExternalParameters();
|
|
Binding.RecordIndex = BaseRecordIndex + RAY_TRACING_SHADER_SLOT_SHADOW;
|
|
Binding.Geometry = DirtyShaderBinding.RayTracingGeometry;
|
|
Binding.SegmentIndex = MeshCommand.GeometrySegmentIndex;
|
|
Binding.ShaderIndexInPipeline = OpaqueMeshDecalHitGroupIndex;
|
|
Binding.BindingType = DirtyShaderBinding.BindingType;
|
|
|
|
}
|
|
else if (MeshCommand.bCastRayTracedShadows && !DirtyShaderBinding.bHidden)
|
|
{
|
|
if (MeshCommand.bOpaque || !bEnableShadowMaterials)
|
|
{
|
|
FRayTracingLocalShaderBindings& Binding = BindingWriter->AddWithExternalParameters();
|
|
Binding.RecordIndex = BaseRecordIndex + RAY_TRACING_SHADER_SLOT_SHADOW;
|
|
Binding.Geometry = DirtyShaderBinding.RayTracingGeometry;
|
|
Binding.SegmentIndex = MeshCommand.GeometrySegmentIndex;
|
|
Binding.ShaderIndexInPipeline = OpaqueShadowMaterialIndex;
|
|
Binding.BindingType = DirtyShaderBinding.BindingType;
|
|
}
|
|
else
|
|
{
|
|
// Masked materials require full material evaluation with any-hit shader.
|
|
// Full CHS is bound, however material evaluation is skipped for shadow rays using a dynamic branch on a ray payload flag.
|
|
MeshCommand.SetRayTracingShaderBindingsForHitGroup(BindingWriter,
|
|
View.ViewUniformBuffer,
|
|
SceneUB,
|
|
NaniteRayTracingUB,
|
|
BaseRecordIndex + RAY_TRACING_SHADER_SLOT_SHADOW,
|
|
DirtyShaderBinding.RayTracingGeometry,
|
|
MeshCommand.GeometrySegmentIndex,
|
|
HitGroupIndex,
|
|
DirtyShaderBinding.BindingType);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FRayTracingLocalShaderBindings& Binding = BindingWriter->AddWithExternalParameters();
|
|
Binding.RecordIndex = BaseRecordIndex + RAY_TRACING_SHADER_SLOT_SHADOW;
|
|
Binding.Geometry = DirtyShaderBinding.RayTracingGeometry;
|
|
Binding.SegmentIndex = MeshCommand.GeometrySegmentIndex;
|
|
Binding.ShaderIndexInPipeline = HiddenMaterialIndex;
|
|
Binding.BindingType = DirtyShaderBinding.BindingType;
|
|
}
|
|
});
|
|
|
|
// For now, only path tracing uses callable shaders (for decals). This is only enabled if the current platform supports callable shaders.
|
|
const bool bCallableShadersRequired = bIsPathTracing && RHISupportsRayTracingCallableShaders(View.Family->GetShaderPlatform());
|
|
if (bCallableShadersRequired)
|
|
{
|
|
FRHIRayTracingShader* DefaultCallableShader = View.ShaderMap->GetShader<FDefaultCallableShader>().GetRayTracingShader();
|
|
const int32 DefaultCallableShaderIndex = FindRayTracingCallableShaderIndex(PipelineState, DefaultCallableShader, true);
|
|
|
|
const uint32 TargetCommandsPerTask = 4096;
|
|
|
|
const uint32 NumTotalCallableCommands = Scene->RayTracingSBT.CallableCommands.Num();
|
|
const uint32 NumTasks = FMath::Max(1u, FMath::DivideAndRoundUp(NumTotalCallableCommands, TargetCommandsPerTask));
|
|
const uint32 CommandsPerTask = FMath::DivideAndRoundUp(NumTotalCallableCommands, NumTasks); // Evenly divide commands between tasks (avoiding potential short last task)
|
|
|
|
View.MaterialRayTracingData.CallableBindings.SetNum(NumTasks);
|
|
|
|
for (uint32 TaskIndex = 0; TaskIndex < NumTasks; ++TaskIndex)
|
|
{
|
|
const uint32 TaskBaseCommandIndex = TaskIndex * CommandsPerTask;
|
|
const FRayTracingShaderCommand* TaskCallableCommands = Scene->RayTracingSBT.CallableCommands.GetData() + TaskBaseCommandIndex;
|
|
const uint32 NumCommands = FMath::Min(CommandsPerTask, NumTotalCallableCommands - TaskBaseCommandIndex);
|
|
|
|
FRayTracingLocalShaderBindingWriter* BindingWriter = new FRayTracingLocalShaderBindingWriter();
|
|
View.MaterialRayTracingData.CallableBindings[TaskIndex] = BindingWriter;
|
|
|
|
GraphBuilder.AddSetupTask(
|
|
[&View, SceneUB, NaniteRayTracingUB, PipelineState, BindingWriter, TaskCallableCommands, NumCommands, bEnableMaterials, DefaultCallableShaderIndex, TaskIndex]()
|
|
{
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(BindRayTracingMaterialPipelineTask);
|
|
|
|
for (uint32 CommandIndex = 0; CommandIndex < NumCommands; ++CommandIndex)
|
|
{
|
|
const FRayTracingShaderCommand& CallableCommand = TaskCallableCommands[CommandIndex];
|
|
|
|
int32 CallableShaderIndex = DefaultCallableShaderIndex; // Force the same shader to be used on all geometry unless materials are enabled
|
|
|
|
if (bEnableMaterials)
|
|
{
|
|
const int32 FoundIndex = FindRayTracingCallableShaderIndex(PipelineState, CallableCommand.Shader, false);
|
|
if (FoundIndex != INDEX_NONE)
|
|
{
|
|
CallableShaderIndex = FoundIndex;
|
|
}
|
|
}
|
|
|
|
CallableCommand.SetRayTracingShaderBindings(
|
|
BindingWriter,
|
|
View.ViewUniformBuffer, SceneUB, NaniteRayTracingUB,
|
|
CallableShaderIndex, CallableCommand.SlotInScene);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
void MergeAndSetRayTracingBindings(
|
|
FRHICommandList& RHICmdList,
|
|
FSceneRenderingBulkObjectAllocator& Allocator,
|
|
FRHIShaderBindingTable* SBT,
|
|
FRayTracingPipelineState* Pipeline,
|
|
TConstArrayView<FRayTracingLocalShaderBindingWriter*> Bindings,
|
|
ERayTracingBindingType BindingType)
|
|
{
|
|
// Gather bindings from all chunks and submit them all as a single batch to allow RHI to bind all shader parameters in parallel.
|
|
|
|
uint32 NumTotalBindings = 0;
|
|
|
|
for (FRayTracingLocalShaderBindingWriter* BindingWriter : Bindings)
|
|
{
|
|
const FRayTracingLocalShaderBindingWriter::FChunk* Chunk = BindingWriter->GetFirstChunk();
|
|
while (Chunk)
|
|
{
|
|
NumTotalBindings += Chunk->Num;
|
|
Chunk = Chunk->Next;
|
|
}
|
|
}
|
|
|
|
if (NumTotalBindings == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const uint32 MergedBindingsSize = sizeof(FRayTracingLocalShaderBindings) * NumTotalBindings;
|
|
FRayTracingLocalShaderBindings* MergedBindings = (FRayTracingLocalShaderBindings*)(RHICmdList.Bypass()
|
|
? Allocator.Malloc(MergedBindingsSize, alignof(FRayTracingLocalShaderBindings))
|
|
: RHICmdList.Alloc(MergedBindingsSize, alignof(FRayTracingLocalShaderBindings)));
|
|
|
|
uint32 MergedBindingIndex = 0;
|
|
for (FRayTracingLocalShaderBindingWriter* BindingWriter : Bindings)
|
|
{
|
|
const FRayTracingLocalShaderBindingWriter::FChunk* Chunk = BindingWriter->GetFirstChunk();
|
|
while (Chunk)
|
|
{
|
|
const uint32 Num = Chunk->Num;
|
|
for (uint32_t i = 0; i < Num; ++i)
|
|
{
|
|
MergedBindings[MergedBindingIndex] = Chunk->Bindings[i];
|
|
MergedBindingIndex++;
|
|
}
|
|
Chunk = Chunk->Next;
|
|
}
|
|
}
|
|
|
|
const bool bCopyDataToInlineStorage = false; // Storage is already allocated from RHICmdList, no extra copy necessary
|
|
RHICmdList.SetBindingsOnShaderBindingTable(
|
|
SBT,
|
|
Pipeline,
|
|
NumTotalBindings, MergedBindings,
|
|
BindingType,
|
|
bCopyDataToInlineStorage);
|
|
}
|
|
|
|
void SetRaytracingShaderBindings(FRHICommandList& RHICmdList, FSceneRenderingBulkObjectAllocator& Allocator, FViewInfo::FRayTracingData& RayTracingData)
|
|
{
|
|
if (!RayTracingData.MaterialBindings.IsEmpty())
|
|
{
|
|
MergeAndSetRayTracingBindings(RHICmdList, Allocator, RayTracingData.ShaderBindingTable, RayTracingData.PipelineState, RayTracingData.MaterialBindings, ERayTracingBindingType::HitGroup);
|
|
}
|
|
if (!RayTracingData.CallableBindings.IsEmpty())
|
|
{
|
|
MergeAndSetRayTracingBindings(RHICmdList, Allocator, RayTracingData.ShaderBindingTable, RayTracingData.PipelineState, RayTracingData.CallableBindings, ERayTracingBindingType::CallableShader);
|
|
}
|
|
|
|
// Move the ray tracing binding container ownership to the command list, so that memory will be
|
|
// released on the RHI thread timeline, after the commands that reference it are processed.
|
|
RHICmdList.EnqueueLambda([PtrsA = MoveTemp(RayTracingData.MaterialBindings),
|
|
PtrsB = MoveTemp(RayTracingData.CallableBindings),
|
|
Mem = MoveTemp(RayTracingData.MaterialBindingsMemory)](FRHICommandList&)
|
|
{
|
|
for (auto Ptr : PtrsA)
|
|
{
|
|
delete Ptr;
|
|
}
|
|
for (auto Ptr : PtrsB)
|
|
{
|
|
delete Ptr;
|
|
}
|
|
});
|
|
}
|
|
|
|
#endif // RHI_RAYTRACING
|