SpliceCardPagesInotTiles:
LumenSceneDirectLightingCulling.usf
StructuredBuffer CardPageIndexAllocator;
StructuredBuffer CardPageIndexData;
RWStructuredBuffer RWCardTileAllocator;
RWStructuredBuffer RWCardTiles;
groupshared uint SharedTileAllocator;
groupshared uint SharedTiles[ 8 * 8 ];
groupshared uint SharedGlobalTileOffset;
[numthreads( 8 , 8 , 1)]
void SpliceCardPagesIntoTilesCS(
uint3 GroupId : SV_GroupID,
uint3 DispatchThreadId : SV_DispatchThreadID,
uint3 GroupThreadId : SV_GroupThreadID)
{
uint LinearThreadIndex = GroupThreadId.x + GroupThreadId.y * 8 ;
if (all(GroupThreadId == 0))
{
SharedTileAllocator = 0;
SharedGlobalTileOffset = 0;
SharedTiles[0] = 0;
}
GroupMemoryBarrierWithGroupSync();
uint LinearLightTileOffset = (GroupId.x % 4);
uint IndexInIndexBuffer = GroupId.x / 4;
uint2 TileOffset = ZOrder2D(LinearThreadIndex, log2(8));
uint2 TileCoord;
TileCoord.x = (LinearLightTileOffset % 2) * 8 + TileOffset.x;
TileCoord.y = (LinearLightTileOffset / 2) * 8 + TileOffset.y;
if (IndexInIndexBuffer < CardPageIndexAllocator[0])
{
uint CardPageIndex = CardPageIndexData[IndexInIndexBuffer];
FLumenCardPageData CardPage = GetLumenCardPageData(CardPageIndex);
if (CardPage.CardIndex >= 0)
{
FLumenCardData Card = GetLumenCardData(CardPage.CardIndex);
const uint2 SizeInTiles = CardPage.SizeInTexels / 8 ;
if (all(TileCoord < SizeInTiles))
{
FCardTileData CardTile;
CardTile.CardPageIndex = CardPageIndex;
CardTile.TileCoord = TileCoord;
uint CardTileIndex = 0;
InterlockedAdd(SharedTileAllocator, 1, CardTileIndex);
SharedTiles[CardTileIndex] = PackCardTileData(CardTile);
}
}
}
GroupMemoryBarrierWithGroupSync();
if (all(GroupThreadId == 0) && SharedTileAllocator > 0)
{
InterlockedAdd(RWCardTileAllocator[0], SharedTileAllocator, SharedGlobalTileOffset);
}
GroupMemoryBarrierWithGroupSync();
if (LinearThreadIndex < SharedTileAllocator)
{
RWCardTiles[SharedGlobalTileOffset + LinearThreadIndex] = SharedTiles[LinearThreadIndex];
}
}
InitializeCardTileIndirectArgs
StructuredBuffer CardTileAllocator;
StructuredBuffer CardTiles;
RWBuffer RWDispatchCardTilesIndirectArgs;
[numthreads( 64 , 1, 1)]
void InitializeCardTileIndirectArgsCS(uint3 DispatchThreadId : SV_DispatchThreadID)
{
if (DispatchThreadId.x == 0)
{
uint NumCardTiles = CardTileAllocator[0];
RWDispatchCardTilesIndirectArgs[0] = (NumCardTiles + 63) / 64;
RWDispatchCardTilesIndirectArgs[1] = 1;
RWDispatchCardTilesIndirectArgs[2] = 1;
RWDispatchCardTilesIndirectArgs[3] = NumCardTiles;
RWDispatchCardTilesIndirectArgs[4] = 1;
RWDispatchCardTilesIndirectArgs[5] = 1;
}
}
BuildLightTiles:
[numthreads( 64 , 1, 1)]
void BuildLightTilesCS(
uint3 GroupId : SV_GroupID,
uint3 DispatchThreadId : SV_DispatchThreadID,
uint3 GroupThreadId : SV_GroupThreadID)
{
uint CardTileIndex = DispatchThreadId.x;
FLightSampleAccumulator LightSampleAccumulator = InitLightSampleAccumulator();
if (CardTileIndex < CardTileAllocator[0])
{
FCardTileData CardTile = UnpackCardTileData(CardTiles[CardTileIndex]);
FLumenCardPageData CardPage = GetLumenCardPageData(CardTile.CardPageIndex);
uint PackedOffsetNum = 0;
if (CardPage.CardIndex >= 0)
{
FLumenCardData Card = GetLumenCardData(CardPage.CardIndex);
const uint2 SizeInTiles = CardPage.SizeInTexels / 8 ;
float2 UVMin = float2(CardTile.TileCoord) / SizeInTiles;
float2 UVMax = float2(CardTile.TileCoord + 1) / SizeInTiles;
float SwapY = UVMin.y;
UVMin.y = 1.0f - UVMax.y;
UVMax.y = 1.0f - SwapY;
uint ViewIndex = GetCardViewIndex(CardPage, Card, UVMin, UVMax, NumViews, true);
for (uint LightIndex = 0; LightIndex < NumLights; ++LightIndex)
{
FLumenLight LumenLight = LoadLumenLight(LightIndex, PreViewTranslation[ViewIndex].xyz);
float3 CardPageWorldCenter = 0.0f;
bool bLightAffectsCard = DoesLightAffectCardPageUVRange(LumenLight, CardPage, Card, UVMin, UVMax, CardPageWorldCenter);
if (bLightAffectsCard)
{
float3 TranslatedWorldPosition = CardPageWorldCenter + LWCToFloat( GetPrimaryView() .PreViewTranslation) ;
FLightSample LightSample;
LightSample.Weight = GetLightWeight(LumenLight, TranslatedWorldPosition);
LightSample.LightIndex = LightIndex;
LightSample.bHasShadowMask = LumenLight.bHasShadowMask;
AddLightSample(LightSampleAccumulator, LightSample);
}
}
uint NumPackedLightSamples = 0;
for (uint PackedSampleIndex = 0; PackedSampleIndex < 8 ; ++PackedSampleIndex)
{
if (LightSampleAccumulator.PackedSamples[PackedSampleIndex] > 0)
{
++NumPackedLightSamples;
}
}
uint LightTileOffset = 0;
if (NumPackedLightSamples > 0)
{
InterlockedAdd(RWLightTileAllocator[0], NumPackedLightSamples, LightTileOffset);
}
for (uint LightSampleIndex = 0; LightSampleIndex < NumPackedLightSamples; ++LightSampleIndex)
{
FLightSample LightSample = UnpackLightSample(LightSampleAccumulator.PackedSamples[LightSampleIndex]);
FLightTileForCompactionPass LightTile;
LightTile.LightIndex = LightSample.LightIndex;
LightTile.ViewIndex = ViewIndex;
LightTile.bHasShadowMask = LightSample.bHasShadowMask;
LightTile.CardTileIndex = CardTileIndex;
LightTile.CulledLightIndex = LightSampleIndex;
RWLightTiles[LightTileOffset + LightSampleIndex] = PackLightTileForCompactionPass(LightTile);
InterlockedAdd(RWLightTileAllocatorPerLight[LightSample.LightIndex * NumViews + ViewIndex], 1);
}
if (NumPackedLightSamples > 0)
{
uint CardLightTilesOffset;
InterlockedAdd(RWLightTileAllocatorForPerCardTileDispatch[0], NumPackedLightSamples, CardLightTilesOffset);
PackedOffsetNum = (NumPackedLightSamples << 24) | CardLightTilesOffset;
}
}
RWLightTileOffsetNumPerCardTile[CardTileIndex] = PackedOffsetNum;
}
}
StructuredBuffer LightTileAllocatorPerLight;
RWStructuredBuffer RWLightTileOffsetsPerLight;
FLumenLight LoadLumenLight(uint LightIndex, float3 PreViewTranslation)
{
FLumenPackedLight PackedLight = LumenPackedLights[LightIndex];
FDeferredLightData DeferredLightData = (FDeferredLightData)0;
DeferredLightData.TranslatedWorldPosition = PackedLight.WorldPosition + PreViewTranslation;
DeferredLightData.InvRadius = PackedLight.InvRadius;
DeferredLightData.Color = PackedLight.Color;
DeferredLightData.FalloffExponent = PackedLight.FalloffExponent;
DeferredLightData.Direction = PackedLight.Direction;
DeferredLightData.Tangent = PackedLight.Tangent;
DeferredLightData.SpotAngles = PackedLight.SpotAngles;
DeferredLightData.SourceRadius = PackedLight.SourceRadius;
DeferredLightData.SourceLength = PackedLight.SourceLength;
DeferredLightData.SoftSourceRadius = PackedLight.SoftSourceRadius;
DeferredLightData.SpecularScale = PackedLight.SpecularScale;
DeferredLightData.ContactShadowLength = 0.0f;
DeferredLightData.ContactShadowLengthInWS = false;
DeferredLightData.DistanceFadeMAD = 0.0f;
DeferredLightData.ShadowMapChannelMask = 0.0f;
DeferredLightData.ShadowedBits = 0;
DeferredLightData.RectLightBarnCosAngle = PackedLight.RectLightBarnCosAngle;
DeferredLightData.RectLightBarnLength = PackedLight.RectLightBarnLength;
DeferredLightData.RectLightAtlasUVScale = PackedLight.SinCosConeAngleOrRectLightAtlasUVScale;
DeferredLightData.RectLightAtlasUVOffset = PackedLight.RectLightAtlasUVOffset;
DeferredLightData.RectLightAtlasMaxLevel = PackedLight.RectLightAtlasMaxLevel;
DeferredLightData.bInverseSquared = PackedLight.FalloffExponent == 0.0f;
DeferredLightData.bRadialLight = PackedLight.LightType != LIGHT_TYPE_DIRECTIONAL;
DeferredLightData.bSpotLight = PackedLight.LightType == LIGHT_TYPE_SPOT;
DeferredLightData.bRectLight = PackedLight.LightType == LIGHT_TYPE_RECT;
FLumenLight LumenLight = (FLumenLight)0;
LumenLight.DeferredLightData = DeferredLightData;
LumenLight.InfluenceSphere = PackedLight.InfluenceSphere;
LumenLight.ProxyPosition = PackedLight.ProxyPosition;
LumenLight.ProxyRadius = PackedLight.ProxyRadius;
LumenLight.ProxyDirection = PackedLight.ProxyDirection;
LumenLight.CosConeAngle = PackedLight.SinCosConeAngleOrRectLightAtlasUVScale.y;
LumenLight.SinConeAngle = PackedLight.SinCosConeAngleOrRectLightAtlasUVScale.x;
LumenLight.VirtualShadowMapId = PackedLight.VirtualShadowMapId;
LumenLight.bHasShadowMask = PackedLight.bHasShadowMask;
LumenLight.LightingChannelMask = PackedLight.LightingChannelMask;
LumenLight.Type = PackedLight.LightType;
return LumenLight;
}
bool DoesLightAffectCardPageUVRange(FLumenLight LumenLight, FLumenCardPageData CardPage, FLumenCardData Card, float2 UVMin, float2 UVMax, inout float3 OutCardPageWorldCenter)
{
// Lighting channels test
if (!(Card.LightingChannelMask & LumenLight.LightingChannelMask))
{
return false;
}
float3 CardPageLocalCenter;
float3 CardPageLocalExtent;
GetCardLocalBBox(CardPage, Card, UVMin, UVMax, CardPageLocalCenter, CardPageLocalExtent);
float3 CardPageWorldCenter = mul(Card.WorldToLocalRotation, CardPageLocalCenter) + Card.Origin;
float3 CardPageWorldExtent = mul(abs(Card.WorldToLocalRotation), CardPageLocalExtent);
float CardPageWorldBoundingSphere = length(CardPageLocalExtent);
OutCardPageWorldCenter = CardPageWorldCenter;
float4 InfluenceSphere = LumenLight.InfluenceSphere;
float3 LightInfluenceSphereLocalCenter = mul(InfluenceSphere.xyz - Card.Origin, Card.WorldToLocalRotation);
const float BoxDistanceSq = ComputeSquaredDistanceFromBoxToPoint(CardPageLocalCenter, CardPageLocalExtent, LightInfluenceSphereLocalCenter);
const bool bCardAffectedByInfluenceSphere = BoxDistanceSq < InfluenceSphere.w * InfluenceSphere.w;
const uint LightType = LumenLight.Type;
const float3 LightPosition = LumenLight.ProxyPosition;
const float3 LightDirection = LumenLight.ProxyDirection;
const float LightRadius = LumenLight.ProxyRadius;
// Fast out
if (LightType != LIGHT_TYPE_DIRECTIONAL && !bCardAffectedByInfluenceSphere)
{
return false;
}
if (LightType == LIGHT_TYPE_DIRECTIONAL)
{
return true;
}
else if (LightType == LIGHT_TYPE_POINT)
{
// Point light
return bCardAffectedByInfluenceSphere;
}
else if (LightType == LIGHT_TYPE_SPOT)
{
float CosConeAngle = LumenLight.CosConeAngle;
float SinConeAngle = LumenLight.SinConeAngle;
float ConeAxisDistance = dot(CardPageWorldCenter - LightPosition, LightDirection);
float2 ConeAxisDistanceMinMax = float2(ConeAxisDistance + CardPageWorldBoundingSphere, ConeAxisDistance - CardPageWorldBoundingSphere);
// Spot light
return bCardAffectedByInfluenceSphere
&& SphereIntersectCone(float4(CardPageWorldCenter, CardPageWorldBoundingSphere), LightPosition, LightDirection, CosConeAngle, SinConeAngle)
&& ConeAxisDistanceMinMax.x > 0 && ConeAxisDistanceMinMax.y < LightRadius;
}
else if (LightType == LIGHT_TYPE_RECT)
{
// Rect light
float4 BackPlane = float4(LightDirection, dot(LightPosition, LightDirection));
float DistanceFromBoxCenterToPlane = dot(BackPlane.xyz, CardPageWorldCenter) - BackPlane.w;
float MaxExtent = dot(CardPageWorldExtent, abs(BackPlane.xyz));
bool bInFrontOfPlane = DistanceFromBoxCenterToPlane + MaxExtent > 0.0f;
return bCardAffectedByInfluenceSphere && bInFrontOfPlane;
}
// Error: Unknown light type
return false;
}
/** Computes squared distance from a point in space to an AABB. */
MaterialFloat ComputeSquaredDistanceFromBoxToPoint(MaterialFloat3 BoxCenter, MaterialFloat3 BoxExtent, MaterialFloat3 InPoint)
{
MaterialFloat3 AxisDistances = max(abs(InPoint - BoxCenter) - BoxExtent, 0);
return dot(AxisDistances, AxisDistances);
}
void GetCardLocalBBox(FLumenCardPageData CardPage, FLumenCardData Card, float2 UVMin, float2 UVMax, out float3 CardPageLocalCenter, out float3 CardPageLocalExtent)
{
float2 CardUVMin = lerp(CardPage.CardUVRect.xw, CardPage.CardUVRect.zy, UVMin);
float2 CardUVMax = lerp(CardPage.CardUVRect.xw, CardPage.CardUVRect.zy, UVMax);
float3 CardPageLocalBoxMin = GetCardLocalPosition(Card.LocalExtent, CardUVMin, 1.0f);
float3 CardPageLocalBoxMax = GetCardLocalPosition(Card.LocalExtent, CardUVMax, 0.0f);
CardPageLocalCenter = 0.5f * (CardPageLocalBoxMax + CardPageLocalBoxMin);
CardPageLocalExtent = 0.5f * (CardPageLocalBoxMax - CardPageLocalBoxMin);
}
float3 GetCardLocalPosition(float3 CardLocalExtent, float2 CardUV, float Depth)
{
CardUV.x = 1.0f - CardUV.x;
float3 LocalPosition;
LocalPosition.xy = CardLocalExtent.xy * (1.0f - 2.0f * CardUV);
LocalPosition.z = -(2.0f * Depth - 1.0f) * CardLocalExtent.z;
return LocalPosition;
}
float GetLightWeight(FLumenLight LumenLight, float3 TranslatedWorldPosition)
{
FDeferredLightData LightData = LumenLight.DeferredLightData;
float Weight = Luminance(LightData.Color);
if (LightData.bRadialLight)
{
float3 L = LightData.Direction;
float3 ToLight = L;
Weight *= GetLocalLightAttenuation(TranslatedWorldPosition, LightData, ToLight, L);
float Attenuation = 1.0f;
if (LightData.bRectLight)
{
FRect Rect = GetRect(ToLight, LightData);
Attenuation = IntegrateLight(Rect);
}
else
{
FCapsuleLight Capsule = GetCapsule(ToLight, LightData);
Capsule.DistBiasSqr = 0;
Attenuation = IntegrateLight(Capsule, LightData.bInverseSquared);
}
Weight *= Attenuation;
}
return Weight;
}
// Build a list of light tiles for future processing
{
FBuildLightTilesCS::FParameters* PassParameters = GraphBuilder.AllocParameters();
PassParameters->IndirectArgBuffer = DispatchCardTilesIndirectArgs;
PassParameters->View = Views[0].ViewUniformBuffer;
PassParameters->LumenCardScene = LumenCardSceneUniformBuffer;
PassParameters->LumenPackedLights = GraphBuilder.CreateSRV(LumenPackedLights);
PassParameters->RWLightTileAllocator = GraphBuilder.CreateUAV(LightTileAllocator);
PassParameters->RWLightTileAllocatorForPerCardTileDispatch = GraphBuilder.CreateUAV(LightTileAllocatorForPerCardTileDispatch);
PassParameters->RWLightTiles = GraphBuilder.CreateUAV(LightTiles);
PassParameters->RWLightTileAllocatorPerLight = GraphBuilder.CreateUAV(LightTileAllocatorPerLight);
PassParameters->RWLightTileOffsetNumPerCardTile = GraphBuilder.CreateUAV(LightTileOffsetNumPerCardTile);
PassParameters->CardTileAllocator = GraphBuilder.CreateSRV(CardTileAllocator);
PassParameters->CardTiles = GraphBuilder.CreateSRV(CardTiles);
PassParameters->MaxLightsPerTile = MaxLightsPerTile;
PassParameters->NumLights = GatheredLights.Num();
PassParameters->NumViews = Views.Num();
check(Views.Num() <= PassParameters->WorldToClip.Num());
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
PassParameters->WorldToClip[ViewIndex] = FMatrix44f(Views[ViewIndex].ViewMatrices.GetViewProjectionMatrix());
PassParameters->PreViewTranslation[ViewIndex] = FVector4f((FVector3f)Views[ViewIndex].ViewMatrices.GetPreViewTranslation(), 0.0f);
}
FBuildLightTilesCS::FPermutationDomain PermutationVector;
PermutationVector.Set(MaxLightsPerTile);
auto ComputeShader = GlobalShaderMap->GetShader(PermutationVector);
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("BuildLightTiles"),
ComputePassFlags,
ComputeShader,
PassParameters,
DispatchCardTilesIndirectArgs,
(uint32)ELumenDispatchCardTilesIndirectArgsOffset::OneThreadPerCardTile);
}