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

1049 lines
43 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "OIT.h"
#include "DataDrivenShaderPlatformInfo.h"
#include "LocalVertexFactory.h"
#include "MeshBatch.h"
#include "OITParameters.h"
#include "Shader.h"
#include "GlobalShader.h"
#include "PrimitiveSceneProxy.h"
#include "ShaderParameters.h"
#include "ShaderParameterStruct.h"
#include "SceneTextureParameters.h"
#include "SceneRendering.h"
#include "ShaderCompilerCore.h"
#include "ShaderPrintParameters.h"
#include "ShaderPrint.h"
#include "ScreenPass.h"
///////////////////////////////////////////////////////////////////////////////////////////////////
// Variables: sorted triangles
static TAutoConsoleVariable<int32> CVarOIT_SortedTriangles_Enable_Project(
TEXT("r.OIT.SortedTriangles"),
1,
TEXT("Enable per-instance triangle sorting to avoid invalid triangle ordering."),
ECVF_ReadOnly | ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarOIT_SortedTriangles_Debug(
TEXT("r.OIT.SortedTriangles.Debug"),
0,
TEXT("Enable per-instance triangle sorting debug rendering."),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarOIT_SortedTriangles_Pool(
TEXT("r.OIT.SortedTriangles.Pool"),
0,
TEXT("Enable index buffer pool allocation which reduce creation/deletion time by re-use buffers."),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarOIT_SortedTriangles_PoolReleaseThreshold(
TEXT("r.OIT.SortedTriangles.Pool.ReleaseFrameThreshold"),
100,
TEXT("Number of frame after which unused buffer are released."),
ECVF_RenderThreadSafe);
///////////////////////////////////////////////////////////////////////////////////////////////////
// Variables: sorted pixels
// Referenced in RendererSettings.h, as it is a project settings
static TAutoConsoleVariable<int32> CVarOIT_SortedPixels_Enable_Project(
TEXT("r.OIT.SortedPixels"),
0,
TEXT("Enable OIT rendering (project settings, can't be changed at runtime)"),
ECVF_ReadOnly | ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarOIT_SortedPixels_Enable_Runtime(
TEXT("r.OIT.SortedPixels.Enable"),
1,
TEXT("Enable OIT rendering (runtime setting, selects shader permutation)"),
ECVF_Scalability | ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarOIT_SortedPixels_PassType(
TEXT("r.OIT.SortedPixels.PassType"),
3,
TEXT("Enable OIT rendering. 0: disable 1: enable OIT for std. translucency 2: enable OIT for separated translucency 3: enable for both std. and separated translucency (default)"),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarOIT_SortedPixels_SampleCount(
TEXT("r.OIT.SortedPixels.MaxSampleCount"),
4,
TEXT("Max sample count."),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarOIT_SortedPixels_Debug(
TEXT("r.OIT.SortedPixels.Debug"),
0,
TEXT("Enable debug rendering for OIT. 1: Enable debug for std. translucency 2: Enable for separated translucency."),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarOIT_SortedPixels_Method(
TEXT("r.OIT.SortedPixels.Method"),
1,
TEXT("Toggle OIT methods 0: Regular alpha-blending (i.e., no OIT) 1: MLAB"),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<float> CVarOIT_SortedPixels_TransmittanceThreshold(
TEXT("r.OIT.SortedPixels.TransmittanceThreshold"),
0.05,
TEXT("Remove translucent rendering surfaces when the accumulated thransmittance is below this threshold"),
ECVF_RenderThreadSafe);
///////////////////////////////////////////////////////////////////////////////////////////////////
static bool IsOITSortedPixelsSupported(EShaderPlatform InShaderPlatform)
{
return FDataDrivenShaderPlatformInfo::GetSupportsROV(InShaderPlatform) && FDataDrivenShaderPlatformInfo::GetSupportsOIT(InShaderPlatform);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// Debug OIT
class FOITPixelDebugCS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FOITPixelDebugCS);
SHADER_USE_PARAMETER_STRUCT(FOITPixelDebugCS, FGlobalShader);
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER(FIntPoint, CursorCoord)
SHADER_PARAMETER(FIntPoint, Resolution)
SHADER_PARAMETER(uint32, MaxSideSampleCount)
SHADER_PARAMETER(uint32, MaxSampleCount)
SHADER_PARAMETER(uint32, Method)
SHADER_PARAMETER(uint32, PassType)
SHADER_PARAMETER(uint32, SupportedPass)
SHADER_PARAMETER_RDG_TEXTURE(Texture2DArray<uint>, SampleDataTexture)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D<uint>, SampleCountTexture)
SHADER_PARAMETER_STRUCT_INCLUDE(ShaderPrint::FShaderParameters, ShaderPrintUniformBuffer)
END_SHADER_PARAMETER_STRUCT()
public:
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return IsOITSortedPixelsSupported(Parameters.Platform) && ShaderPrint::IsSupported(Parameters.Platform); }
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("SHADER_OIT_DEBUG"), 1);
}
};
IMPLEMENT_GLOBAL_SHADER(FOITPixelDebugCS, "/Engine/Private/OITCombine.usf", "MainCS", SF_Compute);
static void AddOITPixelDebugPass(
FRDGBuilder& GraphBuilder,
const FViewInfo& View,
FOITData& OITData)
{
if (!OITData.SampleDataTexture ||
!OITData.SampleCountTexture ||
!ShaderPrint::IsSupported(View.GetShaderPlatform()))
return;
// Force ShaderPrint on.
ShaderPrint::SetEnabled(true);
ShaderPrint::RequestSpaceForCharacters(512);
FOITPixelDebugCS::FParameters* Parameters = GraphBuilder.AllocParameters<FOITPixelDebugCS::FParameters>();
Parameters->PassType = OITData.PassType;
Parameters->SupportedPass = OITData.SupportedPass;
Parameters->Resolution = View.ViewRect.Size();
Parameters->CursorCoord = View.CursorPos;
Parameters->MaxSampleCount = OITData.MaxSamplePerPixel;
Parameters->Method = OITData.Method;
Parameters->MaxSideSampleCount = OITData.MaxSideSamplePerPixel;
Parameters->SampleDataTexture = OITData.SampleDataTexture;
Parameters->SampleCountTexture = OITData.SampleCountTexture;
ShaderPrint::SetParameters(GraphBuilder, View.ShaderPrintData, Parameters->ShaderPrintUniformBuffer);
FOITPixelDebugCS::FPermutationDomain PermutationVector;
TShaderMapRef<FOITPixelDebugCS> ComputeShader(View.ShaderMap, PermutationVector);
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("Translucency::OITDebug(Pixel,%s)", !!(OITData.PassType & OITPass_SeperateTranslucency) ? TEXT("SeparateTransluency") : TEXT("Regular")),
ComputeShader,
Parameters,
FIntVector(1, 1, 1));
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// Combine OIT samples
class FOITPixelCombineCS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FOITPixelCombineCS);
SHADER_USE_PARAMETER_STRUCT(FOITPixelCombineCS, FGlobalShader);
class FModulate : SHADER_PERMUTATION_BOOL("PERMUTATION_MODULATE");
using FPermutationDomain = TShaderPermutationDomain<FModulate>;
BEGIN_SHADER_PARAMETER_STRUCT(FParameters,)
SHADER_PARAMETER(FIntPoint, Resolution)
SHADER_PARAMETER(uint32, MaxSideSampleCount)
SHADER_PARAMETER(uint32, MaxSampleCount)
SHADER_PARAMETER(uint32, Method)
SHADER_PARAMETER(uint32, PassType)
SHADER_PARAMETER_RDG_TEXTURE(Texture2DArray<uint>, SampleDataTexture)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D<uint>, SampleCountTexture)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D<float4>, OutColorTexture)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D<float4>, OutLightingTexture)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D<float4>, OutTransmittanceTexture)
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer)
END_SHADER_PARAMETER_STRUCT()
public:
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return IsOITSortedPixelsSupported(Parameters.Platform); }
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.CompilerFlags.Add(CFLAG_AllowTypedUAVLoads);
OutEnvironment.SetDefine(TEXT("SHADER_OIT_COMBINE"), 1);
}
};
IMPLEMENT_GLOBAL_SHADER(FOITPixelCombineCS, "/Engine/Private/OITCombine.usf", "MainCS", SF_Compute);
static void AddInternalOITComposePass(
FRDGBuilder& GraphBuilder,
const FViewInfo& View,
FOITData& OITData,
FRDGTextureRef OutColorTexture,
FRDGTextureRef OutLightingTexture,
FRDGTextureRef OutTransmittanceTexture)
{
const bool bValidSceneColor = OutColorTexture && OutColorTexture->Desc.NumSamples == 1;
const bool bValidModulateColor = OutLightingTexture && OutLightingTexture->Desc.NumSamples == 1 && OutTransmittanceTexture && OutTransmittanceTexture->Desc.NumSamples == 1;
const bool bModulate = bValidModulateColor;
if (!(bValidSceneColor || bValidModulateColor) ||
!OITData.SampleDataTexture ||
!OITData.SampleCountTexture)
return;
FIntPoint InResolution = OITData.SampleDataTexture->Desc.Extent;
FIntPoint OutResolution = bModulate ? OutLightingTexture->Desc.Extent : OutColorTexture->Desc.Extent;
FIntPoint Resolution = FIntPoint(FMath::Min(InResolution.X, OutResolution.X), FMath::Min(InResolution.Y, OutResolution.Y));
FOITPixelCombineCS::FParameters* Parameters = GraphBuilder.AllocParameters<FOITPixelCombineCS::FParameters>();
Parameters->PassType = OITData.PassType;
Parameters->Resolution = Resolution;
Parameters->Method = OITData.Method;
Parameters->MaxSampleCount = OITData.MaxSamplePerPixel;
Parameters->MaxSideSampleCount = OITData.MaxSideSamplePerPixel;
Parameters->SampleDataTexture = OITData.SampleDataTexture;
Parameters->SampleCountTexture = OITData.SampleCountTexture;
Parameters->ViewUniformBuffer = View.ViewUniformBuffer;
if (bModulate)
{
Parameters->OutLightingTexture = GraphBuilder.CreateUAV(OutLightingTexture);
Parameters->OutTransmittanceTexture = GraphBuilder.CreateUAV(OutTransmittanceTexture);
}
else
{
Parameters->OutColorTexture = GraphBuilder.CreateUAV(OutColorTexture);
}
FOITPixelCombineCS::FPermutationDomain PermutationVector;
PermutationVector.Set<FOITPixelCombineCS::FModulate>(bModulate);
TShaderMapRef<FOITPixelCombineCS > ComputeShader(View.ShaderMap, PermutationVector);
// Add 64 threads permutation
const int32 GroupSize = 8;
const FIntVector DispatchCount = FIntVector(FMath::DivideAndRoundUp(Resolution.X, GroupSize), FMath::DivideAndRoundUp(Resolution.Y, GroupSize), 1);
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("Translucency::OITCombine(%s)", !!(OITData.PassType & OITPass_SeperateTranslucency) ? TEXT("SeparateTransluency") : TEXT("Regular")),
ComputeShader,
Parameters,
DispatchCount);
// Add debug pass
const uint32 ActiveDebugPass = CVarOIT_SortedPixels_Debug.GetValueOnRenderThread();
if (ActiveDebugPass == OITData.PassType)
{
AddOITPixelDebugPass(GraphBuilder, View, OITData);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// OIT Index buffer
class FSortedIndexBuffer : public FIndexBuffer
{
public:
static const uint32 SliceCount = 256u;
FSortedIndexBuffer(uint32 InId, const FIndexBuffer* InSourceIndexBuffer, uint32 InNumIndices, const TCHAR* InDebugName)
: SourceIndexBuffer(InSourceIndexBuffer)
, NumIndices(InNumIndices)
, Id(InId)
, DebugName(InDebugName) { }
bool CanBeInitialized() const
{
return SourceIndexBuffer && SourceIndexBuffer->IndexBufferRHI && SourceIndexBuffer->IndexBufferRHI->GetStride() > 0;
}
virtual void InitRHI(FRHICommandListBase& RHICmdList) override
{
check(SourceIndexBuffer && SourceIndexBuffer->IndexBufferRHI);
const uint32 BytesPerElement = SourceIndexBuffer->IndexBufferRHI->GetStride();
check(BytesPerElement == 2 || BytesPerElement == 4);
const EPixelFormat Format = BytesPerElement == 2 ? PF_R16_UINT : PF_R32_UINT;
const FRHIBufferCreateDesc CreateDesc =
FRHIBufferCreateDesc::CreateIndex(DebugName, NumIndices * BytesPerElement, BytesPerElement)
.AddUsage(EBufferUsageFlags::UnorderedAccess | EBufferUsageFlags::ShaderResource)
.SetInitialState(ERHIAccess::VertexOrIndexBuffer);
IndexBufferRHI = RHICmdList.CreateBuffer(CreateDesc);
SortedIndexUAV = RHICmdList.CreateUnorderedAccessView(
IndexBufferRHI,
FRHIViewDesc::CreateBufferUAV()
.SetType(FRHIViewDesc::EBufferType::Typed)
.SetFormat(Format));
SourceIndexSRV = RHICmdList.CreateShaderResourceView(
SourceIndexBuffer->IndexBufferRHI,
FRHIViewDesc::CreateBufferSRV()
.SetType(FRHIViewDesc::EBufferType::Typed)
.SetFormat(Format));
}
virtual void ReleaseRHI() override
{
IndexBufferRHI.SafeRelease();
SortedIndexUAV.SafeRelease();
SourceIndexSRV.SafeRelease();
}
static constexpr uint32 InvalidId = ~0;
const FIndexBuffer* SourceIndexBuffer = nullptr;
uint32 NumIndices = 0;
uint32 Id = FSortedIndexBuffer::InvalidId;
uint32 LastUsedFrameId = 0;
const TCHAR* DebugName = nullptr;
FShaderResourceViewRHIRef SourceIndexSRV = nullptr;
FUnorderedAccessViewRHIRef SortedIndexUAV = nullptr;
};
static void TrimSortedIndexBuffers(TArray<FSortedIndexBuffer*>& FreeBuffers, TQueue<FSortedIndexBuffer*>& DeleteQueue, uint32 FrameId)
{
uint32 FreeCount = FreeBuffers.Num();
for (uint32 FreeIt = 0; FreeIt < FreeCount;)
{
check(FreeBuffers[FreeIt]);
const uint32 LastFrameId = FreeBuffers[FreeIt]->LastUsedFrameId;
if (LastFrameId != 0)
{
const int32 ElapsedFrame = FMath::Abs(int32(FrameId) - int32(LastFrameId));
if (ElapsedFrame > CVarOIT_SortedTriangles_PoolReleaseThreshold.GetValueOnRenderThread())
{
DeleteQueue.Enqueue(FreeBuffers[FreeIt]);
FreeBuffers[FreeIt] = FreeBuffers[FreeCount - 1];
--FreeCount;
FreeBuffers.SetNum(FreeCount);
continue;
}
}
++FreeIt;
}
}
void FOITSceneData::Allocate(
FRHICommandListBase& RHICmdList,
EPrimitiveType InPrimitiveType,
const FMeshBatchElement& InMeshElement,
FMeshBatchElementDynamicIndexBuffer& OutMeshElement)
{
check(InMeshElement.IndexBuffer && InMeshElement.IndexBuffer->IndexBufferRHI);
check(InPrimitiveType == PT_TriangleList || InPrimitiveType == PT_TriangleStrip);
// Find a free slot, or create a new one
FSortedTriangleData* Out = nullptr;
uint32 FreeSlot = FSortedIndexBuffer::InvalidId;
FreeSlots.Dequeue(FreeSlot);
if (FreeSlot != FSortedIndexBuffer::InvalidId)
{
Out = &Allocations[FreeSlot];
}
else
{
FreeSlot = Allocations.Num();
Out = &Allocations.AddDefaulted_GetRef();
}
// Linear scan if there are some free resource which are large enough
const uint32 NumIndices = InMeshElement.NumPrimitives * 3; // Sorted index always has triangle list topology
FSortedIndexBuffer* OITIndexBuffer = nullptr;
if (CVarOIT_SortedTriangles_Pool.GetValueOnRenderThread() > 0)
{
for (uint32 FreeIt=0,FreeCount=FreeBuffers.Num(); FreeIt<FreeCount; ++FreeIt)
{
FSortedIndexBuffer* FreeBuffer = FreeBuffers[FreeIt];
// TODO: check for format as well + more robust comparison
if (FreeBuffer != nullptr && FreeBuffer->NumIndices >= NumIndices && FreeBuffer->Id == FSortedIndexBuffer::InvalidId)
{
OITIndexBuffer = FreeBuffer;
OITIndexBuffer->Id = FreeSlot;
FreeBuffers[FreeIt] = FreeBuffers[FreeCount - 1];
FreeBuffers.SetNum(FreeCount - 1);
break;
}
}
}
// Otherwise create a new one
if (OITIndexBuffer == nullptr)
{
OITIndexBuffer = new FSortedIndexBuffer(FreeSlot, InMeshElement.IndexBuffer, NumIndices, TEXT("OIT::SortedIndexBuffer"));
// It's possible the index buffer isn't ready yet (data not streamed-in yet). In such case we post-pone OITIndexBuffer creation.
if (OITIndexBuffer->CanBeInitialized())
{
OITIndexBuffer->InitResource(RHICmdList);
}
}
Out->NumPrimitives = InMeshElement.NumPrimitives;
Out->NumIndices = NumIndices;
Out->SourceFirstIndex = InMeshElement.FirstIndex;
Out->SourceBaseVertexIndex = InMeshElement.BaseVertexIndex;
Out->SourceMinVertexIndex = InMeshElement.MinVertexIndex;
Out->SourceMaxVertexIndex = InMeshElement.MaxVertexIndex;
Out->SortedFirstIndex = 0u;
Out->SourcePrimitiveType = InPrimitiveType;
Out->SortedPrimitiveType = PT_TriangleList;
Out->SourceIndexBuffer = InMeshElement.IndexBuffer;
Out->SortedIndexBuffer = OITIndexBuffer;
OutMeshElement.IndexBuffer = Out->SortedIndexBuffer;
OutMeshElement.FirstIndex = Out->SortedFirstIndex;
OutMeshElement.PrimitiveType= Out->SortedPrimitiveType;
}
void FOITSceneData::Deallocate(FMeshBatchElement& OutMeshElement)
{
if (FSortedIndexBuffer* OITIndexBuffer = (FSortedIndexBuffer*)OutMeshElement.DynamicIndexBuffer.IndexBuffer)
{
const uint32 Slot = OITIndexBuffer->Id;
if (Slot < uint32(Allocations.Num()))
{
if (CVarOIT_SortedTriangles_Pool.GetValueOnAnyThread() > 0)
{
OITIndexBuffer->Id = FSortedIndexBuffer::InvalidId;
OITIndexBuffer->LastUsedFrameId = FrameIndex;
FreeBuffers.Add(OITIndexBuffer);
Allocations[Slot] = FSortedTriangleData();
}
else
{
FSortedTriangleData& In = Allocations[Slot];
PendingDeletes.Enqueue((FSortedIndexBuffer*)In.SortedIndexBuffer);
In = FSortedTriangleData();
}
FreeSlots.Enqueue(Slot);
}
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// Sort triangle indices to order them front-to-back or back-to-front
class FOITSortTriangleIndex_ScanCS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FOITSortTriangleIndex_ScanCS);
SHADER_USE_PARAMETER_STRUCT(FOITSortTriangleIndex_ScanCS, FGlobalShader);
class FDebug : SHADER_PERMUTATION_BOOL("PERMUTATION_DEBUG");
using FPermutationDomain = TShaderPermutationDomain<FDebug>;
BEGIN_SHADER_PARAMETER_STRUCT(FParameters,)
// For Debug
SHADER_PARAMETER_STRUCT_INCLUDE(ShaderPrint::FShaderParameters, ShaderPrintParameters)
SHADER_PARAMETER(FMatrix44f, ViewToWorld)
SHADER_PARAMETER(FVector3f, WorldBound_Min)
SHADER_PARAMETER(FVector3f, WorldBound_Max)
SHADER_PARAMETER(FVector3f, ViewBound_Min)
SHADER_PARAMETER(FVector3f, ViewBound_Max)
SHADER_PARAMETER(FMatrix44f, LocalToWorld)
SHADER_PARAMETER(FMatrix44f, LocalToView)
SHADER_PARAMETER(uint32, SourcePrimitiveType)
SHADER_PARAMETER(uint32, SourceFirstIndex)
SHADER_PARAMETER(uint32, SourceBaseVertexIndex)
SHADER_PARAMETER(uint32, SourceMinVertexIndex)
SHADER_PARAMETER(uint32, SourceMaxVertexIndex)
SHADER_PARAMETER(uint32, NumPrimitives)
SHADER_PARAMETER(uint32, NumIndices)
SHADER_PARAMETER(uint32, SortType)
SHADER_PARAMETER(uint32, SortedIndexBufferSizeInByte)
SHADER_PARAMETER(float, ViewBoundMinZ)
SHADER_PARAMETER(float, ViewBoundMaxZ)
SHADER_PARAMETER_SRV(Buffer < float > , PositionBuffer)
SHADER_PARAMETER_SRV(Buffer < uint > , IndexBuffer)
SHADER_PARAMETER_UAV(Buffer < uint > , OutIndexBuffer)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer < uint2 > , OutSliceCounterBuffer)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer < uint > , OutPrimitiveSliceBuffer)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer < uint > , OutDebugData)
END_SHADER_PARAMETER_STRUCT()
public:
static bool SupportDebugMode(EShaderPlatform InPlatform)
{
return ShaderPrint::IsSupported(InPlatform) && !IsHlslccShaderPlatform(InPlatform);
}
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
FPermutationDomain PermutationVector(Parameters.PermutationId);
if (PermutationVector.Get<FDebug>() == 1 && !SupportDebugMode(Parameters.Platform))
{
return false;
}
return OIT::IsSortedTrianglesEnabled(Parameters.Platform);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("SHADER_SCAN"), 1);
OutEnvironment.SetDefine(TEXT("SORTING_SLICE_COUNT"), FSortedIndexBuffer::SliceCount);
}
static EShaderPermutationPrecacheRequest ShouldPrecachePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
FPermutationDomain PermutationVector(Parameters.PermutationId);
if (PermutationVector.Get<FDebug>())
{
return EShaderPermutationPrecacheRequest::NotPrecached;
}
return FGlobalShader::ShouldPrecachePermutation(Parameters);
}
};
IMPLEMENT_GLOBAL_SHADER(FOITSortTriangleIndex_ScanCS, "/Engine/Private/OIT/OITSorting.usf", "MainCS", SF_Compute);
///////////////////////////////////////////////////////////////////////////////////////////////////
class FOITSortTriangleIndex_AllocateCS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FOITSortTriangleIndex_AllocateCS);
SHADER_USE_PARAMETER_STRUCT(FOITSortTriangleIndex_AllocateCS, FGlobalShader);
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer<uint>, SliceCounterBuffer)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer<uint>, SliceOffsetsBuffer)
END_SHADER_PARAMETER_STRUCT()
public:
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return OIT::IsSortedTrianglesEnabled(Parameters.Platform);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("SHADER_ALLOCATE"), 1);
OutEnvironment.SetDefine(TEXT("SORTING_SLICE_COUNT"), FSortedIndexBuffer::SliceCount);
}
};
IMPLEMENT_GLOBAL_SHADER(FOITSortTriangleIndex_AllocateCS, "/Engine/Private/OIT/OITSorting.usf", "MainCS", SF_Compute);
///////////////////////////////////////////////////////////////////////////////////////////////////
class FOITSortTriangleIndex_WriteOutCS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FOITSortTriangleIndex_WriteOutCS);
SHADER_USE_PARAMETER_STRUCT(FOITSortTriangleIndex_WriteOutCS, FGlobalShader);
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER(uint32, SourcePrimitiveType)
SHADER_PARAMETER(uint32, NumPrimitives)
SHADER_PARAMETER(uint32, NumIndices)
SHADER_PARAMETER(uint32, SrcFirstIndex)
SHADER_PARAMETER(uint32, DstFirstIndex)
SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer<uint>, SliceOffsetsBuffer)
SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer<uint>, PrimitiveSliceBuffer)
SHADER_PARAMETER_SRV(Buffer<uint>, IndexBuffer)
SHADER_PARAMETER_UAV(RWBuffer<uint>, OutIndexBuffer)
END_SHADER_PARAMETER_STRUCT()
public:
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return OIT::IsSortedTrianglesEnabled(Parameters.Platform);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("SHADER_WRITE"), 1);
OutEnvironment.SetDefine(TEXT("SORTING_SLICE_COUNT"), FSortedIndexBuffer::SliceCount);
}
};
IMPLEMENT_GLOBAL_SHADER(FOITSortTriangleIndex_WriteOutCS, "/Engine/Private/OIT/OITSorting.usf", "MainCS", SF_Compute);
///////////////////////////////////////////////////////////////////////////////////////////////////
class FOITSortTriangleIndex_Debug : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FOITSortTriangleIndex_Debug);
SHADER_USE_PARAMETER_STRUCT(FOITSortTriangleIndex_Debug, FGlobalShader);
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_INCLUDE(ShaderPrint::FShaderParameters, ShaderPrintParameters)
SHADER_PARAMETER(uint32, VisibleInstances)
SHADER_PARAMETER(uint32, VisiblePrimitives)
SHADER_PARAMETER(uint32, VisibleIndexSizeInBytes)
SHADER_PARAMETER(uint32, AllocatedBuffers)
SHADER_PARAMETER(uint32, AllocatedIndexSizeInBytes)
SHADER_PARAMETER(uint32, UnusedBuffers)
SHADER_PARAMETER(uint32, UnusedIndexSizeInBytes)
SHADER_PARAMETER(uint32, TotalEntries)
SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer<uint>, DebugData)
END_SHADER_PARAMETER_STRUCT()
public:
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return FOITSortTriangleIndex_ScanCS::SupportDebugMode(Parameters.Platform);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("SHADER_DEBUG"), 1);
OutEnvironment.SetDefine(TEXT("SORTING_SLICE_COUNT"), FSortedIndexBuffer::SliceCount);
}
static EShaderPermutationPrecacheRequest ShouldPrecachePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return EShaderPermutationPrecacheRequest::NotPrecached;
}
};
IMPLEMENT_GLOBAL_SHADER(FOITSortTriangleIndex_Debug, "/Engine/Private/OIT/OITSorting.usf", "MainCS", SF_Compute);
///////////////////////////////////////////////////////////////////////////////////////////////////
struct FOITDebugData
{
static const EPixelFormat Format = PF_R32_UINT;
FRDGBufferRef Buffer = nullptr; // First element is counter, then element are: (NumPrim/Type/Size)
uint32 VisibleInstances = 0;
uint32 VisiblePrimitives = 0;
uint32 VisibleIndexSizeInBytes = 0;
uint32 AllocatedBuffers = 0;
uint32 AllocatedIndexSizeInBytes = 0;
uint32 UnusedBuffers = 0;
uint32 UnusedIndexSizeInBytes = 0;
uint32 TotalEntries = 0;
bool IsValid() const { return Buffer != nullptr; }
};
static void AddOITSortTriangleIndexPass(
FRDGBuilder& GraphBuilder,
const FViewInfo& View,
const FOITSceneData& OITSceneData,
const FSortedTrianglesMeshBatch& MeshBatch,
FTriangleSortingOrder SortType,
FOITDebugData& DebugData)
{
// Fat format: PF_R32G32_UINT | Compact format: PF_R32_UINT
const EPixelFormat PackedFormat = PF_R32_UINT;
const uint32 PackedFormatInBytes = 4;
const bool bIsValid =
MeshBatch.Mesh != nullptr &&
MeshBatch.Mesh->VertexFactory != nullptr &&
MeshBatch.Mesh->VertexFactory->SupportsTriangleSorting() &&
MeshBatch.Mesh->Elements.Num() > 0 &&
MeshBatch.Mesh->Elements[0].DynamicIndexBuffer.IndexBuffer != nullptr;
if (!bIsValid)
{
return;
}
// If the index buffer hasn't been initialized yet (e.g., data are not streamed in yet), run initialization
FSortedIndexBuffer* SortedIndexBuffer = (FSortedIndexBuffer*)MeshBatch.Mesh->Elements[0].DynamicIndexBuffer.IndexBuffer;
if (!SortedIndexBuffer->IsInitialized())
{
if (!SortedIndexBuffer->CanBeInitialized())
{
// Data are still not ready yet
return;
}
SortedIndexBuffer->InitResource(GraphBuilder.RHICmdList);
}
const FShaderResourceViewRHIRef VertexPosition = MeshBatch.Mesh->VertexFactory->GetTriangleSortingPositionSRV();
if (!VertexPosition) { return; }
const FSortedIndexBuffer* OITIndexBuffer = (const FSortedIndexBuffer*)MeshBatch.Mesh->Elements[0].DynamicIndexBuffer.IndexBuffer;
check(OITIndexBuffer->Id < uint32(OITSceneData.Allocations.Num()));
const FSortedTriangleData& Allocation = OITSceneData.Allocations[OITIndexBuffer->Id];
check(Allocation.IsValid());
check(Allocation.SourcePrimitiveType == PT_TriangleList || Allocation.SourcePrimitiveType == PT_TriangleStrip);
check(Allocation.SortedPrimitiveType == PT_TriangleList);
FRDGBufferRef PrimitiveSliceBuffer = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateBufferDesc(PackedFormatInBytes, Allocation.NumPrimitives), TEXT("OIT.TriangleSortingSliceIndex"));
FRDGBufferRef SliceCounterBuffer = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateBufferDesc(4, FSortedIndexBuffer::SliceCount), TEXT("OIT.SliceCounters"));
FRDGBufferRef SliceOffsetsBuffer = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateBufferDesc(4, FSortedIndexBuffer::SliceCount), TEXT("OIT.SliceOffsets"));
FRDGBufferUAVRef SliceCounterUAV = GraphBuilder.CreateUAV(SliceCounterBuffer, PF_R32_UINT);
AddClearUAVPass(GraphBuilder, SliceCounterUAV, 0u);
const EShaderPlatform Platform = View.Family->GetShaderPlatform();
const bool bDebugEnable = DebugData.IsValid() && FOITSortTriangleIndex_ScanCS::SupportDebugMode(Platform) && ShaderPrint::IsValid(View.ShaderPrintData);
FRHIBuffer* SortedIndexBufferRHI = Allocation.SortedIndexBuffer->IndexBufferRHI;
// 1. Scan the primitive and assign each primitive to a slice
{
// Compute the primitive Min/Max-Z value in view space. This domain is sliced for sorting
const FBoxSphereBounds& Bounds = MeshBatch.Proxy->GetBounds();
const FBoxSphereBounds& ViewBounds = Bounds.TransformBy(View.ViewMatrices.GetViewMatrix());
const float ViewBoundMinZ = ViewBounds.GetBox().Min.Z;
const float ViewBoundMaxZ = ViewBounds.GetBox().Max.Z;
FOITSortTriangleIndex_ScanCS::FParameters* Parameters = GraphBuilder.AllocParameters<FOITSortTriangleIndex_ScanCS::FParameters>();
Parameters->LocalToView = FMatrix44f(MeshBatch.Proxy->GetLocalToWorld() * View.ViewMatrices.GetViewMatrix());
Parameters->LocalToWorld = FMatrix44f(MeshBatch.Proxy->GetLocalToWorld());
Parameters->SourcePrimitiveType = Allocation.SourcePrimitiveType == PT_TriangleStrip ? 1u : 0u;
Parameters->NumPrimitives = Allocation.NumPrimitives;
Parameters->NumIndices = Allocation.NumIndices;
Parameters->ViewBoundMinZ = ViewBoundMinZ;
Parameters->ViewBoundMaxZ = ViewBoundMaxZ;
Parameters->SortType = SortType == FTriangleSortingOrder::BackToFront ? 0 : 1;
Parameters->SortedIndexBufferSizeInByte = Allocation.SortedIndexBuffer->IndexBufferRHI->GetSize();
Parameters->PositionBuffer = VertexPosition;
Parameters->SourceFirstIndex = Allocation.SourceFirstIndex;
Parameters->SourceBaseVertexIndex = Allocation.SourceBaseVertexIndex;
Parameters->SourceMinVertexIndex = Allocation.SourceMinVertexIndex;
Parameters->SourceMaxVertexIndex = Allocation.SourceMaxVertexIndex;
Parameters->IndexBuffer = Allocation.SortedIndexBuffer->SourceIndexSRV;
Parameters->OutIndexBuffer = Allocation.SortedIndexBuffer->SortedIndexUAV;
Parameters->OutSliceCounterBuffer = SliceCounterUAV;
Parameters->OutPrimitiveSliceBuffer = GraphBuilder.CreateUAV(PrimitiveSliceBuffer, PackedFormat);
// Debug
if (bDebugEnable)
{
Parameters->ViewToWorld = FMatrix44f(View.ViewMatrices.GetViewMatrix().Inverse()); // LWC_TODO: Precision loss?
Parameters->WorldBound_Min = (FVector3f)Bounds.GetBox().Min;
Parameters->WorldBound_Max = (FVector3f)Bounds.GetBox().Max;
Parameters->ViewBound_Min = (FVector3f)ViewBounds.GetBox().Min;
Parameters->ViewBound_Max = (FVector3f)ViewBounds.GetBox().Max;
ShaderPrint::SetParameters(GraphBuilder, View.ShaderPrintData, Parameters->ShaderPrintParameters);
check(DebugData.Buffer);
++DebugData.VisibleInstances;
DebugData.VisiblePrimitives += Parameters->NumPrimitives;
DebugData.VisibleIndexSizeInBytes += Allocation.SortedIndexBuffer->IndexBufferRHI->GetSize();
Parameters->OutDebugData = GraphBuilder.CreateUAV(DebugData.Buffer, PF_R32_UINT);
}
FOITSortTriangleIndex_ScanCS::FPermutationDomain PermutationVector;
PermutationVector.Set<FOITSortTriangleIndex_ScanCS::FDebug>(bDebugEnable ? 1 : 0);
TShaderMapRef<FOITSortTriangleIndex_ScanCS> ComputeShader(View.ShaderMap, PermutationVector);
const uint32 GroupSize = FSortedIndexBuffer::SliceCount;
const FIntVector DispatchCount = FIntVector(FMath::DivideAndRoundUp(Parameters->NumPrimitives, GroupSize), 1u, 1u);
check(DispatchCount.X < GRHIMaxDispatchThreadGroupsPerDimension.X);
ClearUnusedGraphResources(ComputeShader, Parameters);
GraphBuilder.AddPass(
RDG_EVENT_NAME("OIT::SortTriangleIndices(Scan)"),
Parameters,
ERDGPassFlags::Compute,
[Parameters, ComputeShader, DispatchCount, SortedIndexBufferRHI](FRDGAsyncTask, FRHIComputeCommandList& RHICmdList)
{
RHICmdList.Transition(FRHITransitionInfo(SortedIndexBufferRHI, ERHIAccess::VertexOrIndexBuffer, ERHIAccess::UAVCompute));
FComputeShaderUtils::Dispatch(RHICmdList, ComputeShader, *Parameters, DispatchCount);
});
}
// 2. Pre-fix sum onto the slices count to allocate each bucket
{
FOITSortTriangleIndex_AllocateCS::FParameters* Parameters = GraphBuilder.AllocParameters<FOITSortTriangleIndex_AllocateCS::FParameters>();
Parameters->SliceCounterBuffer = GraphBuilder.CreateSRV(SliceCounterBuffer, PF_R32_UINT);
Parameters->SliceOffsetsBuffer = GraphBuilder.CreateUAV(SliceOffsetsBuffer, PF_R32_UINT);
TShaderMapRef<FOITSortTriangleIndex_AllocateCS> ComputeShader(View.ShaderMap);
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("OIT::SortTriangleIndices(PrefixedSum)"),
ComputeShader,
Parameters,
FIntVector(1u, 1u, 1u));
}
// 3. Write-out the sorted indices
{
FOITSortTriangleIndex_WriteOutCS::FParameters* Parameters = GraphBuilder.AllocParameters<FOITSortTriangleIndex_WriteOutCS::FParameters>();
Parameters->SourcePrimitiveType = Allocation.SourcePrimitiveType == PT_TriangleStrip ? 1u : 0u;
Parameters->NumPrimitives = Allocation.NumPrimitives;
Parameters->NumIndices = Allocation.NumIndices;
Parameters->SrcFirstIndex = Allocation.SourceFirstIndex;
Parameters->DstFirstIndex = 0u;
Parameters->SliceOffsetsBuffer = GraphBuilder.CreateSRV(SliceOffsetsBuffer, PF_R32_UINT);
Parameters->PrimitiveSliceBuffer = GraphBuilder.CreateSRV(PrimitiveSliceBuffer, PackedFormat);
Parameters->IndexBuffer = Allocation.SortedIndexBuffer->SourceIndexSRV;
Parameters->OutIndexBuffer = Allocation.SortedIndexBuffer->SortedIndexUAV;
TShaderMapRef<FOITSortTriangleIndex_WriteOutCS> ComputeShader(View.ShaderMap);
const uint32 GroupSize = 256;
const FIntVector DispatchCount = FIntVector(FMath::DivideAndRoundUp(Parameters->NumPrimitives, GroupSize), 1u, 1u);
ClearUnusedGraphResources(ComputeShader, Parameters);
GraphBuilder.AddPass(
RDG_EVENT_NAME("OIT::SortTriangleIndices(Write)"),
Parameters,
ERDGPassFlags::Compute,
[Parameters, ComputeShader, DispatchCount, SortedIndexBufferRHI](FRDGAsyncTask, FRHIComputeCommandList& RHICmdList)
{
FComputeShaderUtils::Dispatch(RHICmdList, ComputeShader, *Parameters, DispatchCount);
RHICmdList.Transition(FRHITransitionInfo(SortedIndexBufferRHI, ERHIAccess::UAVCompute, ERHIAccess::VertexOrIndexBuffer));
});
}
// Next todos
// * Merge several meshes together (not clear out to do the mapping thread->mesh info)
// * Batch Scan/Alloc/Write of several primitive, so that we have better overlapping
}
static void AddOITTriangleDebugPass(
FRDGBuilder& GraphBuilder,
const FViewInfo& View,
const FOITDebugData& DebugData)
{
const EShaderPlatform Platform = View.Family->GetShaderPlatform();
if (!DebugData.IsValid() || !ShaderPrint::IsSupported(Platform))
{
return;
}
// Force ShaderPrint on.
ShaderPrint::SetEnabled(true);
ShaderPrint::RequestSpaceForCharacters(DebugData.VisibleInstances * 256 + 512);
ShaderPrint::RequestSpaceForLines(DebugData.VisiblePrimitives * 8 + DebugData.VisibleInstances * 32);
FOITSortTriangleIndex_Debug::FParameters* Parameters = GraphBuilder.AllocParameters<FOITSortTriangleIndex_Debug::FParameters>();
Parameters->VisibleInstances = DebugData.VisibleInstances;
Parameters->VisiblePrimitives = DebugData.VisiblePrimitives;
Parameters->VisibleIndexSizeInBytes = DebugData.VisibleIndexSizeInBytes;
Parameters->UnusedBuffers = DebugData.UnusedBuffers;
Parameters->UnusedIndexSizeInBytes = DebugData.UnusedIndexSizeInBytes;
Parameters->AllocatedBuffers = DebugData.AllocatedBuffers;
Parameters->AllocatedIndexSizeInBytes = DebugData.AllocatedIndexSizeInBytes;
Parameters->TotalEntries = DebugData.TotalEntries;
Parameters->DebugData = GraphBuilder.CreateSRV(DebugData.Buffer, FOITDebugData::Format);
ShaderPrint::SetParameters(GraphBuilder, View.ShaderPrintData, Parameters->ShaderPrintParameters);
TShaderMapRef<FOITSortTriangleIndex_Debug> ComputeShader(View.ShaderMap);
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("OIT::Debug(Triangle)"),
ComputeShader,
Parameters,
FIntVector(1u, 1u, 1u));
}
///////////////////////////////////////////////////////////////////////////////////////////////////
namespace OIT
{
bool IsSortedTrianglesEnabled(EShaderPlatform InPlatform)
{
return CVarOIT_SortedTriangles_Enable_Project.GetValueOnAnyThread() > 0 && !IsOpenGLPlatform(InPlatform);
}
bool IsSortedPixelsEnabledForProject(EShaderPlatform InPlatform)
{
//const bool bMSAAEnabled = GetDefaultAntiAliasingMethod(GetMaxSupportedFeatureLevel(InPlatform)) != EAntiAliasingMethod::AAM_MSAA;
const bool bPixelOIT = CVarOIT_SortedPixels_Enable_Project.GetValueOnAnyThread() > 0 && FDataDrivenShaderPlatformInfo::GetSupportsOIT(EShaderPlatform(InPlatform));
return bPixelOIT;
}
bool InternalIsSortedPixelsEnabled(EShaderPlatform InPlatform, bool bMSAA)
{
return IsSortedPixelsEnabledForProject(InPlatform) && GRHISupportsRasterOrderViews && !!CVarOIT_SortedPixels_Enable_Runtime.GetValueOnRenderThread() && !bMSAA;
}
bool IsSortedPixelsEnabled(const FViewInfo& InView) { return InternalIsSortedPixelsEnabled(InView.GetShaderPlatform(), InView.AntiAliasingMethod == EAntiAliasingMethod::AAM_MSAA); }
bool IsSortedPixelsEnabled(EShaderPlatform InPlatform) { return InternalIsSortedPixelsEnabled(InPlatform, false /*bMSAA*/); }
bool IsSortedPixelsEnabledForPass(EOITPassType PassType)
{
const uint32 PassTypeBits = FMath::Clamp(CVarOIT_SortedPixels_PassType.GetValueOnRenderThread(), 0, 3);
return !!(PassTypeBits & PassType);
}
bool IsCompatible(const FMeshBatch& InMesh, ERHIFeatureLevel::Type InFeatureLevel)
{
return InMesh.IsTranslucent(InFeatureLevel) && InMesh.VertexFactory && InMesh.VertexFactory->SupportsTriangleSorting();
}
void AddSortTrianglesPass(FRDGBuilder& GraphBuilder, const FViewInfo& View, FOITSceneData& OITSceneData, FTriangleSortingOrder SortType)
{
if (!IsSortedTrianglesEnabled(View.GetShaderPlatform()))
{
return;
}
RDG_EVENT_SCOPE(GraphBuilder, "OIT::IndexSorting");
const bool bDebugEnable = CVarOIT_SortedTriangles_Debug.GetValueOnRenderThread() > 0;
FOITDebugData DebugData;
if (bDebugEnable)
{
const uint32 ValidAllocationCount = OITSceneData.Allocations.Num();
DebugData.Buffer = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateBufferDesc(4u, 10*ValidAllocationCount + 1), TEXT("OIT.DebugData"));
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(DebugData.Buffer, FOITDebugData::Format), 0u);
// Allocated/used
uint32 AllocatedNumPrimitives = 0;
for (const FSortedTriangleData& Allocated : OITSceneData.Allocations)
{
if (Allocated.IsValid())
{
++DebugData.AllocatedBuffers;
DebugData.AllocatedIndexSizeInBytes += Allocated.SortedIndexBuffer->IndexBufferRHI->GetSize();
AllocatedNumPrimitives += Allocated.NumPrimitives;
}
}
ShaderPrint::SetEnabled(true);
ShaderPrint::RequestSpaceForCharacters(DebugData.AllocatedBuffers * 256 + 512);
ShaderPrint::RequestSpaceForLines(AllocatedNumPrimitives * 8 + DebugData.AllocatedBuffers * 32);
// Unused
for (const FSortedIndexBuffer* FreeBuffer : OITSceneData.FreeBuffers)
{
++DebugData.UnusedBuffers;
DebugData.UnusedIndexSizeInBytes = FreeBuffer->IndexBufferRHI->GetSize();
}
DebugData.TotalEntries = OITSceneData.Allocations.Num();
}
for (const FSortedTrianglesMeshBatch& MeshBatch : View.SortedTrianglesMeshBatches)
{
AddOITSortTriangleIndexPass(GraphBuilder, View, OITSceneData, MeshBatch, SortType, DebugData);
}
if (DebugData.IsValid())
{
AddOITTriangleDebugPass(GraphBuilder, View, DebugData);
}
// Trim unused buffers
OITSceneData.FrameIndex = View.Family->FrameNumber;
if (CVarOIT_SortedTriangles_Pool.GetValueOnRenderThread() > 0 && CVarOIT_SortedTriangles_PoolReleaseThreshold.GetValueOnRenderThread() > 0)
{
TrimSortedIndexBuffers(OITSceneData.FreeBuffers, OITSceneData.PendingDeletes, OITSceneData.FrameIndex);
}
}
FOITData CreateOITData(FRDGBuilder& GraphBuilder, const FViewInfo& View, EOITPassType PassType)
{
const bool bOIT = IsSortedPixelsEnabled(View);
const uint32 PassTypeBits = FMath::Clamp(CVarOIT_SortedPixels_PassType.GetValueOnRenderThread(), 0, 3);
const bool bPassValid = !!(PassTypeBits & PassType);
const uint32 LayerCount = 3u; /*Depth/Color/Trans*/
FOITData Out;
if (!bOIT || !bPassValid)
{
Out.MaxSideSamplePerPixel = 0;
Out.MaxSamplePerPixel = 0;
Out.Method = 0;
Out.TransmittanceThreshold = 0;
Out.SampleDataTexture = GraphBuilder.CreateTexture(FRDGTextureDesc::Create2DArray(FIntPoint(1, 1), PF_R32_UINT, FClearValueBinding::None, TexCreate_ShaderResource | TexCreate_UAV, LayerCount), TEXT("OIT.SampleData"));
Out.SampleCountTexture = GraphBuilder.CreateTexture(FRDGTextureDesc::Create2D(FIntPoint(1, 1), PF_R32_UINT, FClearValueBinding::Black, TexCreate_ShaderResource | TexCreate_UAV), TEXT("OIT.SampleCount"));
return Out;
}
// Scene render targets might not exist yet; avoids NaNs.
FIntPoint EffectiveBufferSize = View.GetSceneTexturesConfig().Extent;
EffectiveBufferSize.X = FMath::Max(EffectiveBufferSize.X, 1);
EffectiveBufferSize.Y = FMath::Max(EffectiveBufferSize.Y, 1);
// Allocate OIT data
Out.PassType = PassTypeBits & PassType ? PassType : OITPass_None;
if (PassTypeBits & OITPass_RegularTranslucency) Out.SupportedPass |= OITPass_RegularTranslucency;
if (PassTypeBits & OITPass_SeperateTranslucency) Out.SupportedPass |= OITPass_SeperateTranslucency;
if (Out.PassType != OITPass_None)
{
// Round sample to be square to store them into square tile
uint32 MaxSamplePerPixel = FMath::Clamp(CVarOIT_SortedPixels_SampleCount.GetValueOnRenderThread(), 1, 16);
uint32 MaxSideSamplePerPixel = FMath::FloorToInt(FMath::Sqrt(float(MaxSamplePerPixel)));
MaxSamplePerPixel = MaxSideSamplePerPixel * MaxSideSamplePerPixel;
Out.MaxSideSamplePerPixel = MaxSideSamplePerPixel;
Out.MaxSamplePerPixel = MaxSamplePerPixel;
Out.Method = FMath::Clamp(CVarOIT_SortedPixels_Method.GetValueOnRenderThread(), 0, 1);
Out.TransmittanceThreshold = FMath::Clamp(CVarOIT_SortedPixels_TransmittanceThreshold.GetValueOnRenderThread(), 0.f, 1.f);
Out.SampleDataTexture = GraphBuilder.CreateTexture(FRDGTextureDesc::Create2DArray(EffectiveBufferSize * MaxSideSamplePerPixel, PF_R32_UINT, FClearValueBinding::None, TexCreate_ShaderResource | TexCreate_UAV, LayerCount), TEXT("OIT.SampleData"));
Out.SampleCountTexture = GraphBuilder.CreateTexture(FRDGTextureDesc::Create2D(EffectiveBufferSize, PF_R32_UINT, FClearValueBinding::None, TexCreate_ShaderResource | TexCreate_UAV), TEXT("OIT.SampleCount"));
}
// TODO: Add tile clear based on the coarse translucent raster AABB buffer
//AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(Out.SampleDataTexture), 0u);
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(Out.SampleCountTexture), 0u);
return Out;
}
void SetOITParameters(FRDGBuilder& GraphBuilder, const FViewInfo& View, FOITBasePassUniformParameters& OutOIT, const FOITData& InOITData)
{
// Always set the parameters, even when the OIT is disabled, as Uniform buffer validation complain about null resources otherwise
OutOIT.OITMethod = InOITData.Method;
OutOIT.bOITEnable = InOITData.PassType != OITPass_None ? 1 : 0;
OutOIT.MaxSideSamplePerPixel = InOITData.MaxSideSamplePerPixel;
OutOIT.MaxSamplePerPixel = InOITData.MaxSamplePerPixel;
OutOIT.TransmittanceThreshold = InOITData.TransmittanceThreshold;
OutOIT.OutOITSampleData = GraphBuilder.CreateUAV(InOITData.SampleDataTexture);
OutOIT.OutOITSampleCount = GraphBuilder.CreateUAV(InOITData.SampleCountTexture);
}
void AddOITComposePass(FRDGBuilder& GraphBuilder, const FViewInfo& View, FOITData& OITData, FRDGTextureRef SceneColorTexture)
{
AddInternalOITComposePass(GraphBuilder, View, OITData, SceneColorTexture, nullptr, nullptr);
}
void AddOITComposePass(FRDGBuilder& GraphBuilder, const FViewInfo& View, FOITData& OITData, FRDGTextureRef LightingTexture, FRDGTextureRef ModulateTexture)
{
AddInternalOITComposePass(GraphBuilder, View, OITData, nullptr, LightingTexture, ModulateTexture);
}
void OnRenderBegin(FOITSceneData& OITSceneData)
{
// Delete all enqueue buffer for deletion
FSortedIndexBuffer* Buffer = nullptr;
while (OITSceneData.PendingDeletes.Dequeue(Buffer))
{
if (Buffer)
{
Buffer->ReleaseResource();
delete Buffer;
Buffer = nullptr;
}
}
}
}