Files
UnrealEngine/Engine/Source/Runtime/Renderer/Private/RayTracing/RayTracingMaterialHitShaders.h
2025-05-18 13:04:45 +08:00

334 lines
12 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "RHIDefinitions.h"
#if RHI_RAYTRACING
#include "DataDrivenShaderPlatformInfo.h"
#include "LightMapRendering.h"
#include "MaterialDomain.h"
#include "MeshMaterialShader.h"
#include "MeshPassProcessor.inl"
#include "RayTracingMeshDrawCommands.h"
#include "RayTracingInstanceMask.h"
#include "RayTracingPayloadType.h"
#include "RayTracing/RayTracing.h"
#include "ShaderParameterStruct.h"
#include "SceneRendering.h"
#include <type_traits>
FRHIRayTracingShader* GetRayTracingDefaultMissShader(const FGlobalShaderMap* ShaderMap);
FRHIRayTracingShader* GetRayTracingDefaultOpaqueShader(const FGlobalShaderMap* ShaderMap);
FRHIRayTracingShader* GetRayTracingDefaultHiddenShader(const FGlobalShaderMap* ShaderMap);
class FRayTracingMeshProcessor
{
public:
RENDERER_API FRayTracingMeshProcessor(FRayTracingMeshCommandContext* InCommandContext, const FScene* InScene, const FSceneView* InViewIfDynamicMeshCommand, ERayTracingType InRayTracingType);
RENDERER_API ~FRayTracingMeshProcessor();
RENDERER_API void AddMeshBatch(const FMeshBatch& RESTRICT MeshBatch, uint64 BatchElementMask, const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy);
private:
FRayTracingMeshCommandContext* CommandContext;
const FScene* Scene;
const FSceneView* ViewIfDynamicMeshCommand;
ERHIFeatureLevel::Type FeatureLevel;
ERayTracingType RayTracingType;
bool Process(
const FMeshBatch& RESTRICT MeshBatch,
uint64 BatchElementMask,
const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy,
const FMaterialRenderProxy& RESTRICT MaterialRenderProxy,
const FMaterial& RESTRICT MaterialResource,
const FUniformLightMapPolicy& RESTRICT LightMapPolicy);
template<typename RayTracingShaderType, typename ShaderElementDataType>
void BuildRayTracingMeshCommands(
const FMeshBatch& RESTRICT MeshBatch,
uint64 BatchElementMask,
const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy,
const FMaterialRenderProxy& RESTRICT MaterialRenderProxy,
const FMaterial& RESTRICT MaterialResource,
const TShaderRef<RayTracingShaderType>& RayTracingShader,
const ShaderElementDataType& ShaderElementData)
{
const FVertexFactory* RESTRICT VertexFactory = MeshBatch.VertexFactory;
checkf(MaterialRenderProxy.ImmutableSamplerState.ImmutableSamplers[0] == nullptr, TEXT("Immutable samplers not yet supported in Mesh Draw Command pipeline"));
FRayTracingMeshCommand SharedCommand;
SetupRayTracingMeshCommandMaskAndStatus(SharedCommand, MeshBatch, PrimitiveSceneProxy, MaterialResource, RayTracingType);
if (GRHISupportsRayTracingShaders)
{
SharedCommand.SetShader(RayTracingShader);
}
FVertexInputStreamArray VertexStreams;
VertexFactory->GetStreams(FeatureLevel, EVertexInputStreamType::Default, VertexStreams);
if (RayTracingShader.IsValid())
{
FMeshDrawSingleShaderBindings ShaderBindings = SharedCommand.ShaderBindings.GetSingleShaderBindings(SF_RayHitGroup);
RayTracingShader->GetShaderBindings(Scene, FeatureLevel, PrimitiveSceneProxy, MaterialRenderProxy, MaterialResource, ShaderElementData, ShaderBindings);
}
const int32 NumElements = MeshBatch.Elements.Num();
for (int32 BatchElementIndex = 0; BatchElementIndex < NumElements; BatchElementIndex++)
{
if ((1ull << BatchElementIndex) & BatchElementMask)
{
const FMeshBatchElement& BatchElement = MeshBatch.Elements[BatchElementIndex];
FRayTracingMeshCommand& RayTracingMeshCommand = CommandContext->AddCommand(SharedCommand);
if (RayTracingShader.IsValid())
{
FMeshDrawSingleShaderBindings RayHitGroupShaderBindings = RayTracingMeshCommand.ShaderBindings.GetSingleShaderBindings(SF_RayHitGroup);
FMeshMaterialShader::GetElementShaderBindings(RayTracingShader, Scene, ViewIfDynamicMeshCommand, VertexFactory, EVertexInputStreamType::Default, FeatureLevel, PrimitiveSceneProxy, MeshBatch, BatchElement, ShaderElementData, RayHitGroupShaderBindings, VertexStreams);
// Command can only be cached if no global/static uniform buffers are used - if all platforms use SBTLayout for all RT shaders then this could be a check
RayTracingMeshCommand.bCanBeCached = !RayTracingMeshCommand.HasGlobalUniformBufferBindings();
}
RayTracingMeshCommand.GeometrySegmentIndex = uint32(MeshBatch.SegmentIndex) + BatchElementIndex;
RayTracingMeshCommand.bIsTranslucent = MeshBatch.IsTranslucent(MaterialResource.GetFeatureLevel());
CommandContext->FinalizeCommand(RayTracingMeshCommand);
}
}
}
bool ProcessPathTracing(
const FMeshBatch& RESTRICT MeshBatch,
uint64 BatchElementMask,
const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy,
const FMaterialRenderProxy& RESTRICT MaterialRenderProxy,
const FMaterial& RESTRICT MaterialResource);
bool TryAddMeshBatch(
const FMeshBatch& RESTRICT MeshBatch,
uint64 BatchElementMask,
const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy,
int32 StaticMeshId,
const FMaterialRenderProxy& MaterialRenderProxy,
const FMaterial& Material
);
};
class FHiddenMaterialHitGroup : public FGlobalShader
{
DECLARE_EXPORTED_GLOBAL_SHADER(FHiddenMaterialHitGroup, RENDERER_API)
SHADER_USE_ROOT_PARAMETER_STRUCT(FHiddenMaterialHitGroup, FGlobalShader)
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return ShouldCompileRayTracingShadersForProject(Parameters.Platform);
}
static ERayTracingPayloadType GetRayTracingPayloadType(const int32 PermutationId)
{
return ERayTracingPayloadType::RayTracingMaterial;
}
static const FShaderBindingLayout* GetShaderBindingLayout(const FShaderPermutationParameters& Parameters)
{
return RayTracing::GetShaderBindingLayout(Parameters.Platform);
}
using FParameters = FEmptyShaderParameters;
};
class FOpaqueShadowHitGroup : public FGlobalShader
{
DECLARE_EXPORTED_GLOBAL_SHADER(FOpaqueShadowHitGroup, RENDERER_API)
SHADER_USE_ROOT_PARAMETER_STRUCT(FOpaqueShadowHitGroup, FGlobalShader)
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return ShouldCompileRayTracingShadersForProject(Parameters.Platform);
}
static ERayTracingPayloadType GetRayTracingPayloadType(const int32 PermutationId)
{
return ERayTracingPayloadType::RayTracingMaterial;
}
static const FShaderBindingLayout* GetShaderBindingLayout(const FShaderPermutationParameters& Parameters)
{
return RayTracing::GetShaderBindingLayout(Parameters.Platform);
}
using FParameters = FEmptyShaderParameters;
};
class FDefaultCallableShader : public FGlobalShader
{
DECLARE_EXPORTED_GLOBAL_SHADER(FDefaultCallableShader, RENDERER_API)
SHADER_USE_ROOT_PARAMETER_STRUCT(FDefaultCallableShader, FGlobalShader)
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return ShouldCompileRayTracingCallableShadersForProject(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;
};
class FRayTracingLocalShaderBindingWriter
{
public:
FRayTracingLocalShaderBindingWriter()
{}
FRayTracingLocalShaderBindingWriter(const FRayTracingLocalShaderBindingWriter&) = delete;
FRayTracingLocalShaderBindingWriter& operator = (const FRayTracingLocalShaderBindingWriter&) = delete;
FRayTracingLocalShaderBindingWriter(FRayTracingLocalShaderBindingWriter&&) = delete;
FRayTracingLocalShaderBindingWriter& operator = (FRayTracingLocalShaderBindingWriter&&) = delete;
~FRayTracingLocalShaderBindingWriter() = default;
FRayTracingLocalShaderBindings& AddWithInlineParameters(uint32 NumUniformBuffers, uint32 LooseDataSize = 0)
{
FRayTracingLocalShaderBindings* Result = AllocateInternal();
if (NumUniformBuffers)
{
uint32 AllocSize = sizeof(FRHIUniformBuffer*) * NumUniformBuffers;
Result->UniformBuffers = (FRHIUniformBuffer**)ParameterMemory.Alloc(AllocSize, alignof(FRHIUniformBuffer*));
FMemory::Memset(Result->UniformBuffers, 0, AllocSize);
}
Result->NumUniformBuffers = NumUniformBuffers;
if (LooseDataSize)
{
Result->LooseParameterData = (uint8*)ParameterMemory.Alloc(LooseDataSize, alignof(void*));
}
Result->LooseParameterDataSize = LooseDataSize;
return *Result;
}
FRayTracingLocalShaderBindings& AddWithExternalParameters()
{
return *AllocateInternal();
}
void Commit(FRHICommandList& RHICmdList, FRHIShaderBindingTable* SBT, FRayTracingPipelineState* Pipeline, bool bCopyDataToInlineStorage) const
{
const FChunk* Chunk = FirstChunk;
while (Chunk)
{
RHICmdList.SetRayTracingHitGroups(SBT, Pipeline, Chunk->Num, Chunk->Bindings, bCopyDataToInlineStorage);
Chunk = Chunk->Next;
}
}
struct FChunk
{
static constexpr uint32 MaxNum = 1024;
// Note: constructors for elements of this array are called explicitly in AllocateInternal(). Destructors are not called.
static_assert(std::is_trivially_destructible_v<FRayTracingLocalShaderBindings>, "FRayTracingLocalShaderBindings must be trivially destructible, as no destructor will be called.");
FRayTracingLocalShaderBindings Bindings[MaxNum];
FChunk* Next;
uint32 Num;
};
const FChunk* GetFirstChunk() const
{
return FirstChunk;
}
private:
FChunk* FirstChunk = nullptr;
FChunk* CurrentChunk = nullptr;
FMemStackBase ParameterMemory;
friend class FRHICommandList;
FRayTracingLocalShaderBindings* AllocateInternal()
{
if (!CurrentChunk || CurrentChunk->Num == FChunk::MaxNum)
{
FChunk* OldChunk = CurrentChunk;
static_assert(std::is_trivially_destructible_v<FChunk>, "Chunk must be trivially destructible, as no destructor will be called.");
CurrentChunk = (FChunk*)ParameterMemory.Alloc(sizeof(FChunk), alignof(FChunk));
CurrentChunk->Next = nullptr;
CurrentChunk->Num = 0;
if (FirstChunk == nullptr)
{
FirstChunk = CurrentChunk;
}
if (OldChunk)
{
OldChunk->Next = CurrentChunk;
}
}
FRayTracingLocalShaderBindings* ResultMemory = &CurrentChunk->Bindings[CurrentChunk->Num++];
return new(ResultMemory) FRayTracingLocalShaderBindings;
}
};
template <typename FunctionType>
void AddRayTracingLocalShaderBindingWriterTasks(
FRDGBuilder& GraphBuilder,
TConstArrayView<FRayTracingShaderBindingData> DirtyPersistentRayTracingShaderBindings,
TArray<FRayTracingLocalShaderBindingWriter*, SceneRenderingAllocator>& ShaderBindingWriters,
FunctionType SetupBindingsFunction)
{
const uint32 NumTotalDirtyBindings = DirtyPersistentRayTracingShaderBindings.Num();
const uint32 TargetBindingsPerTask = 1024;
const uint32 NumTasks = FMath::Max(1u, FMath::DivideAndRoundUp(NumTotalDirtyBindings, TargetBindingsPerTask));
const uint32 BindingsPerTask = FMath::DivideAndRoundUp(NumTotalDirtyBindings, NumTasks); // Evenly divide commands between tasks (avoiding potential short last task)
ShaderBindingWriters.SetNum(NumTasks);
for (uint32 TaskIndex = 0; TaskIndex < NumTasks; ++TaskIndex)
{
const uint32 FirstTaskBindingIndex = TaskIndex * BindingsPerTask;
const FRayTracingShaderBindingData* RTShaderBindings = DirtyPersistentRayTracingShaderBindings.GetData() + FirstTaskBindingIndex;
const uint32 NumBindings = FMath::Min(BindingsPerTask, NumTotalDirtyBindings - FirstTaskBindingIndex);
FRayTracingLocalShaderBindingWriter* BindingWriter = new FRayTracingLocalShaderBindingWriter();
ShaderBindingWriters[TaskIndex] = BindingWriter;
GraphBuilder.AddSetupTask(
[BindingWriter, RTShaderBindings, NumBindings, SetupBindingsFunction]()
{
TRACE_CPUPROFILER_EVENT_SCOPE(BuildRayTracingMaterialBindingsTask);
for (uint32 BindingIndex = 0; BindingIndex < NumBindings; ++BindingIndex)
{
const FRayTracingShaderBindingData& RTShaderBindingData = RTShaderBindings[BindingIndex];
SetupBindingsFunction(RTShaderBindingData, BindingWriter);
}
});
}
}
void SetRaytracingShaderBindings(FRHICommandList& RHICmdList, FSceneRenderingBulkObjectAllocator& Allocator, FViewInfo::FRayTracingData& RayTracingData);
#endif // RHI_RAYTRACING