Files
UnrealEngine/Engine/Shaders/Private/TemporalSuperResolution/TSRWeightRelaxation.usf
2025-05-18 13:04:45 +08:00

274 lines
9.0 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
//------------------------------------------------------- INCLUDES
#include "TSRColorSpace.ush"
//#include "TSRDepthVelocityAnalysis.ush"
#include "TSRThinGeometryCommon.ush"
//------------------------------------------------------- CONFIG
#define DEBUG_ARRAY_SIZE 8
#define DEBUG_DISABLE_PADDING 0
#define DEBUG_DIGESTABLE 0
#define CONFIG_SKY_RELAXATION (DIM_SKY_RELAXATION)
//------------------------------------------------------- CONSTANTS
#if CONFIG_LDS_DESPILL
static const uint kDespillInput = LDS_DESPILL_THREAD_COUNT * LDS_DESPILL_OFFSET * 0;
static const uint kDespillHistory = LDS_DESPILL_THREAD_COUNT * LDS_DESPILL_OFFSET * 1;
#endif
//------------------------------------------------------- PARAMETERS
int TileOverscan;
int ThinGeometryTextureIndex;
float ErrorMultiplier;
float MaxRelaxationWeight;
Texture2D<tsr_half> CurrentCoverageTexture;
Texture2D<tsr_half> InputMoireLumaTexture;
Texture2D<tsr_halfC> InputTexture;
Texture2D<tsr_half4> InputSceneTranslucencyTexture;
RWTexture2DArray<uint> R8Output;
//------------------------------------------------------- GLOBALS
static uint2 GGroupId = 0;
//------------------------------------------------------- DEBUG
static tsr_tensor_half4 Debug[DEBUG_ARRAY_SIZE];
//------------------------------------------------------- INPUT FETCHES
tsr_short2 ComputeElementPixelPos(const uint ElementIndex)
{
tsr_short2 ClampedFetchPixelPos = ComputeElementInputPixelPos(
/* LaneStride = */ uint2(LANE_STRIDE_X, LANE_STRIDE_Y),
TileOverscan,
InputInfo_ViewportMin,
InputInfo_ViewportMax,
GGroupId,
GGroupThreadIndex,
ElementIndex);
return ClampedFetchPixelPos;
}
tsr_tensor_short2 ComputePixelPos()
{
tsr_tensor_short2 PixelPos;
UNROLL_N(SIMD_SIZE)
for (uint ElementIndex = 0; ElementIndex < SIMD_SIZE; ElementIndex++)
{
PixelPos.SetElement(ElementIndex, ComputeElementPixelPos(ElementIndex));
}
return PixelPos;
}
tsr_short2 ComputeElementOutputPixelPos(const uint ElementIndex)
{
return ComputeElementOutputPixelPos(
/* LaneStride = */ uint2(LANE_STRIDE_X, LANE_STRIDE_Y),
TileOverscan,
InputInfo_ViewportMin,
InputInfo_ViewportMax,
GGroupId,
GGroupThreadIndex,
ElementIndex);
}
CALL_SITE_DEBUGLOC
tsr_half ComputeRGBLuma(tsr_half3 Input)
{
// aligns to the logic in MeasureFlickeringLuma.usf so taht we can compare the
// difference between LumaBeforeComposition that comes from that pass.
tsr_half3 LinearColor = Input;
tsr_half3 SMColor = LinearToSMCS(LinearColor);
tsr_half SMLuma = dot(SMColor, kMoireSMCSWeights);
tsr_half GLuma = SMCSToGCS(SMLuma.rrr).r;
return GLuma;
}
CALL_SITE_DEBUGLOC
tsr_tensor_half ComputeRGBLuma(tsr_tensor_half3 Input)
{
tsr_tensor_half Luma;
UNROLL_N(SIMD_SIZE)
for (uint ElementIndex = 0; ElementIndex < SIMD_SIZE; ElementIndex++)
{
// aligns to the logic in MeasureFlickeringLuma.usf so taht we can compare the
// difference between LumaBeforeComposition that comes from that pass.
tsr_half3 LinearColor = Input.GetElement(ElementIndex);
Luma.SetElement(ElementIndex, ComputeRGBLuma(LinearColor));
}
return Luma;
}
void FetchInput(
tsr_tensor_short2 PixelPos,
out tsr_tensor_half InputLuma,
out tsr_tensor_half2 Translucency,
out tsr_tensor_half HistoryRelaxationWeight,
out tsr_tensor_bool IsClusterHoleRegion
#if CONFIG_SKY_RELAXATION
,out tsr_tensor_half InputLumaBeforeComposition
,out tsr_tensor_half CurrentCoverage
#endif
)
{
tsr_tensor_ushort ThinGeometryCache;
UNROLL_N(SIMD_SIZE)
for (uint ElementIndex = 0; ElementIndex < SIMD_SIZE; ElementIndex++)
{
tsr_short2 ClampedFetchPixelPos = PixelPos.GetElement(ElementIndex);
InputLuma.SetElement(ElementIndex,ComputeRGBLuma(InputTexture[ClampedFetchPixelPos].xyz));
tsr_half4 FullTranslucency = InputSceneTranslucencyTexture[ClampedFetchPixelPos];
Translucency.SetElement(ElementIndex, tsr_half2(ComputeRGBLuma(FullTranslucency.rgb),FullTranslucency.a));
ThinGeometryCache.SetElement(ElementIndex, tsr_ushort(R8Output[int3(ClampedFetchPixelPos, ThinGeometryTextureIndex)]));
#if CONFIG_SKY_RELAXATION
CurrentCoverage.SetElement(ElementIndex,CurrentCoverageTexture[ClampedFetchPixelPos]);
InputLumaBeforeComposition.SetElement(ElementIndex,InputMoireLumaTexture[ClampedFetchPixelPos]);
#endif
}
DecodeThinGeometry(ThinGeometryCache, HistoryRelaxationWeight, IsClusterHoleRegion);
}
//------------------------------------------------------- ENTRY POINT
#if COMPILER_SUPPORTS_WAVE_SIZE && DIM_WAVE_SIZE > 0
WAVESIZE(DIM_WAVE_SIZE)
#endif
[numthreads(LANE_COUNT * WAVE_COUNT, 1, 1)]
void MainCS(
uint2 GroupId : SV_GroupID,
uint GroupThreadIndex : SV_GroupIndex)
{
GGroupId = GroupId;
GGroupThreadIndex = GroupThreadIndex;
#if DEBUG_OUTPUT
{
UNROLL_N(DEBUG_ARRAY_SIZE)
for (uint DebugId = 0; DebugId < DEBUG_ARRAY_SIZE; DebugId++)
{
Debug[DebugId].SetAllElements(0.0);
}
}
#endif
tsr_tensor_short2 PixelPos = ComputePixelPos();
tsr_tensor_half InputLuma;
tsr_tensor_half2 Translucency;
tsr_tensor_half HistoryRelaxationWeight;
tsr_tensor_bool IsPartialCoverageRegion;
#if CONFIG_SKY_RELAXATION
tsr_tensor_half InputLumaBeforeComposition;
tsr_tensor_half CurrentCoverage;
#endif
FetchInput(
PixelPos,
/*out */ InputLuma,
/*out */ Translucency,
/*out */ HistoryRelaxationWeight,
/*out */ IsPartialCoverageRegion
#if CONFIG_SKY_RELAXATION
,/*out */ InputLumaBeforeComposition
,/*out */ CurrentCoverage
#endif
);
// Edge case: thin geometry against volumetric clouds.
#if CONFIG_SKY_RELAXATION
tsr_tensor_bool IsEarlyTranslucencyEffect;
{
tsr_tensor_half EarlyTranslucencyAE = abs(InputLuma - InputLumaBeforeComposition);
#if DEBUG_OUTPUT
Debug[0].SetComponent(0, EarlyTranslucencyAE);
#endif
tsr_tensor_half MinCurrentCoverageNeighbor;
MinCurrentCoverageNeighbor = Min3x3(CurrentCoverage);
tsr_tensor_bool bIsLeafAgainstSky = and(and(EarlyTranslucencyAE > tsr_tensor_half::Const(0.3), InputLumaBeforeComposition > tsr_tensor_half::Const(0.01)),
and(MinCurrentCoverageNeighbor == tsr_tensor_half::Const(0), IsThinGeometry(CurrentCoverage)));
#if DEBUG_OUTPUT
Debug[0].SetComponent(1, select(bIsLeafAgainstSky,abs(InputLuma - InputLumaBeforeComposition), tsr_tensor_half::Const(0)));
#endif
IsEarlyTranslucencyEffect = bIsLeafAgainstSky;
tsr_tensor_half EarlyTranslucencyAddWeight = select(IsEarlyTranslucencyEffect, tsr_tensor_half::Const(1.0), tsr_tensor_half::Const(0));
EarlyTranslucencyAddWeight = WeightedAvg3x3(EarlyTranslucencyAddWeight, float3(0.8, 1.0, 0.8));
IsPartialCoverageRegion = select(EarlyTranslucencyAddWeight > tsr_tensor_half::Const(0),tsr_tensor_bool::Const(false),IsPartialCoverageRegion);
HistoryRelaxationWeight = saturate(HistoryRelaxationWeight + EarlyTranslucencyAddWeight);
}
#endif
// If translucency has data turn down the weights aggresively. if alpha < 0.95
tsr_tensor_half TranslucencyRatio = Translucency[0] * rcp(Translucency[0] + InputLuma * Translucency[1] + tsr_tensor_half::Const(0.0005));
// Weights decline starts around TranslucencyRatio = 0.05, and declines fast to 0 when Translucency ratio goes to 0.6.
tsr_tensor_half RatioToLerpWeight = max(tsr_tensor_half::Const(0.6)-TranslucencyRatio,tsr_tensor_half::Const(0));
RatioToLerpWeight = min(RatioToLerpWeight * RatioToLerpWeight * tsr_tensor_half::Const(3.33), tsr_tensor_half::Const(1));
RatioToLerpWeight = select(or(IsPartialCoverageRegion,TranslucencyRatio > tsr_tensor_half::Const(0.6)), RatioToLerpWeight, tsr_tensor_half::Const(1));
HistoryRelaxationWeight = lerp(tsr_tensor_half::Const(0), HistoryRelaxationWeight, RatioToLerpWeight);
tsr_tensor_ushort EncodedThinGeometry = EncodeThinGeometry(HistoryRelaxationWeight,IsPartialCoverageRegion);
// Output
UNROLL_N(SIMD_SIZE)
for(uint ElementIndex = 0; ElementIndex < SIMD_SIZE; ElementIndex++)
{
tsr_short2 OutputPixelPos = ComputeElementOutputPixelPos(ElementIndex);
R8Output[int3(OutputPixelPos, ThinGeometryTextureIndex)] = EncodedThinGeometry.GetElement(ElementIndex);
}
#if DEBUG_OUTPUT
{
// Mark the top left corner of the tile for debugging purposes.
#if DEBUG_DISABLE_PADDING == 2
if (GGroupThreadIndex == 0)
{
UNROLL_N(DEBUG_ARRAY_SIZE)
for (uint DebugId = 0; DebugId < DEBUG_ARRAY_SIZE; DebugId++)
{
Debug[DebugId].SetElement(/* ElementIndex = */ 0, 1.0);
}
}
#endif // DEBUG_DISABLE_PADDING == 2
UNROLL_N(SIMD_SIZE)
for (uint ElementIndex = 0; ElementIndex < SIMD_SIZE; ElementIndex++)
{
const tsr_short2 OutputPixelPos = ComputeElementOutputPixelPos(
/* LaneStride = */ uint2(LANE_STRIDE_X, LANE_STRIDE_Y),
TileOverscan,
InputInfo_ViewportMin,
InputInfo_ViewportMax,
GGroupId,
GGroupThreadIndex,
ElementIndex,
/* OutputDataOffset = */ int2(0, 0),
/* OutputResDivisor = */ 1,
/* bDebugDisablePadding = */ DEBUG_DISABLE_PADDING != 0);
UNROLL_N(DEBUG_ARRAY_SIZE)
for (uint DebugId = 0; DebugId < DEBUG_ARRAY_SIZE; DebugId++)
{
DebugOutput[tsr_short3(OutputPixelPos, DebugId)] = float4(Debug[DebugId].GetElement(ElementIndex));
}
}
}
#endif // DEBUG_OUTPUT
}