619 lines
20 KiB
HLSL
619 lines
20 KiB
HLSL
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "../Common.ush"
|
|
#include "../IntersectionUtils.ush"
|
|
#define SUPPORT_CONTACT_SHADOWS 0
|
|
#include "../DeferredLightingCommon.ush"
|
|
|
|
#include "LumenCardCommon.ush"
|
|
#include "LumenCardTile.ush"
|
|
#include "LumenSceneDirectLighting.ush"
|
|
#include "SurfaceCache/LumenSurfaceCache.ush"
|
|
|
|
#ifndef THREADGROUP_SIZE
|
|
#define THREADGROUP_SIZE 1
|
|
#endif
|
|
|
|
#ifndef MAX_LIGHT_SAMPLES
|
|
#define MAX_LIGHT_SAMPLES 1
|
|
#endif
|
|
|
|
StructuredBuffer<uint> CardPageIndexAllocator;
|
|
StructuredBuffer<uint> CardPageIndexData;
|
|
RWStructuredBuffer<uint> RWCardTileAllocator;
|
|
RWStructuredBuffer<uint> RWCardTiles;
|
|
|
|
#ifdef SpliceCardPagesIntoTilesCS
|
|
|
|
groupshared uint SharedTileAllocator;
|
|
groupshared uint SharedTiles[THREADGROUP_SIZE * THREADGROUP_SIZE];
|
|
groupshared uint SharedGlobalTileOffset;
|
|
|
|
/**
|
|
* Splice card pages into N card tiles
|
|
*/
|
|
[numthreads(THREADGROUP_SIZE, THREADGROUP_SIZE, 1)]
|
|
void SpliceCardPagesIntoTilesCS(
|
|
uint3 GroupId : SV_GroupID,
|
|
uint3 DispatchThreadId : SV_DispatchThreadID,
|
|
uint3 GroupThreadId : SV_GroupThreadID)
|
|
{
|
|
uint LinearThreadIndex = GroupThreadId.x + GroupThreadId.y * THREADGROUP_SIZE;
|
|
if (all(GroupThreadId == 0))
|
|
{
|
|
SharedTileAllocator = 0;
|
|
SharedGlobalTileOffset = 0;
|
|
SharedTiles[0] = 0;
|
|
}
|
|
|
|
GroupMemoryBarrierWithGroupSync();
|
|
|
|
// One thread per tile
|
|
uint LinearLightTileOffset = (GroupId.x % 4);
|
|
uint IndexInIndexBuffer = GroupId.x / 4;
|
|
|
|
// Improve tile coherency
|
|
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 / CARD_TILE_SIZE;
|
|
|
|
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];
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
StructuredBuffer<uint> CardTileAllocator;
|
|
StructuredBuffer<uint> CardTiles;
|
|
RWBuffer<uint> RWDispatchCardTilesIndirectArgs;
|
|
|
|
#ifdef InitializeCardTileIndirectArgsCS
|
|
|
|
[numthreads(THREADGROUP_SIZE, 1, 1)]
|
|
void InitializeCardTileIndirectArgsCS(uint3 DispatchThreadId : SV_DispatchThreadID)
|
|
{
|
|
if (DispatchThreadId.x == 0)
|
|
{
|
|
uint NumCardTiles = CardTileAllocator[0];
|
|
|
|
// One thread per card tile
|
|
WriteDispatchIndirectArgs(RWDispatchCardTilesIndirectArgs, 0, DivideAndRoundUp64(NumCardTiles), 1, 1);
|
|
|
|
// One thread group per card tile
|
|
WriteDispatchIndirectArgs(RWDispatchCardTilesIndirectArgs, 1, NumCardTiles, 1, 1);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef CalculateCardTileDepthRangesCS
|
|
|
|
RWStructuredBuffer<uint> RWCardTileDepthRanges;
|
|
|
|
groupshared uint SharedMinTileDepth;
|
|
groupshared uint SharedMaxTileDepth;
|
|
|
|
[numthreads(THREADGROUP_SIZE, THREADGROUP_SIZE, 1)]
|
|
void CalculateCardTileDepthRangesCS(
|
|
uint GroupId : SV_GroupID,
|
|
uint2 GroupThreadId : SV_GroupThreadID)
|
|
{
|
|
uint CardTileIndex = GroupId;
|
|
|
|
if (GroupThreadId.x == 0 && GroupThreadId.y == 0)
|
|
{
|
|
SharedMinTileDepth = 0xFFFFFFFF;
|
|
SharedMaxTileDepth = 0;
|
|
}
|
|
|
|
GroupMemoryBarrierWithGroupSync();
|
|
|
|
if (CardTileIndex < CardTileAllocator[0])
|
|
{
|
|
FCardTileData CardTile = UnpackCardTileData(CardTiles[CardTileIndex]);
|
|
FLumenCardPageData CardPage = GetLumenCardPageData(CardTile.CardPageIndex);
|
|
|
|
if (CardPage.CardIndex >= 0)
|
|
{
|
|
uint2 TexelInCardPageCoord = CardTile.TileCoord * CARD_TILE_SIZE + GroupThreadId.xy;
|
|
float2 AtlasUV = CardPage.PhysicalAtlasUVRect.xy + CardPage.PhysicalAtlasUVTexelScale * (TexelInCardPageCoord + 0.5f);
|
|
float Depth = Texture2DSampleLevel(LumenCardScene.DepthAtlas, GlobalPointClampedSampler, AtlasUV, 0).x;
|
|
|
|
if (IsSurfaceCacheDepthValid(Depth))
|
|
{
|
|
InterlockedMax(SharedMaxTileDepth, asuint(Depth));
|
|
InterlockedMin(SharedMinTileDepth, asuint(Depth));
|
|
}
|
|
}
|
|
}
|
|
|
|
GroupMemoryBarrierWithGroupSync();
|
|
|
|
if (GroupThreadId.x == 0 && GroupThreadId.y == 0)
|
|
{
|
|
float MinTileDepth = SharedMinTileDepth == 0xFFFFFFFF ? 0.0f : asfloat(SharedMinTileDepth);
|
|
float MaxTileDepth = SharedMaxTileDepth == 0 ? 1.0f : asfloat(SharedMaxTileDepth);
|
|
RWCardTileDepthRanges[CardTileIndex] = f32tof16(MinTileDepth) | (f32tof16(MaxTileDepth) << 16);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
uint CullToCardTileDepthRange;
|
|
StructuredBuffer<uint> CardTileDepthRanges;
|
|
|
|
float2 GetCardTileDepthRange(uint CardTileIndex)
|
|
{
|
|
if (CullToCardTileDepthRange)
|
|
{
|
|
uint Packed = CardTileDepthRanges[CardTileIndex];
|
|
return float2(f16tof32(Packed & 0xFFFF), f16tof32(Packed >> 16));
|
|
}
|
|
|
|
return float2(0.0f, 1.0f);
|
|
}
|
|
|
|
bool DoesLightInfluenceSphereAffectCardPageUVRange(float4 InfluenceSphere, FLumenCardPageData CardPage, FLumenCardData Card, float2 UVMin, float2 UVMax, float2 CardTileDepthRange)
|
|
{
|
|
float3 CardPageLocalCenter;
|
|
float3 CardPageLocalExtent;
|
|
GetCardLocalBBox(CardPage, Card, UVMin, UVMax, CardTileDepthRange, CardPageLocalCenter, CardPageLocalExtent);
|
|
|
|
float3 LightInfluenceSphereLocalCenter = mul(InfluenceSphere.xyz - Card.Origin, Card.WorldToLocalRotation);
|
|
const float BoxDistanceSq = ComputeSquaredDistanceFromBoxToPoint(CardPageLocalCenter, CardPageLocalExtent, LightInfluenceSphereLocalCenter);
|
|
return BoxDistanceSq < InfluenceSphere.w * InfluenceSphere.w;
|
|
}
|
|
|
|
// This function doesn't test light influence spheres
|
|
bool DoesLightAffectCardPageUVRange(FLumenLight LumenLight, FLumenCardPageData CardPage, FLumenCardData Card, float2 UVMin, float2 UVMax, float2 CardTileDepthRange, inout float3 OutCardPageWorldCenter)
|
|
{
|
|
// Lighting channels test
|
|
if (!(Card.LightingChannelMask & LumenLight.LightingChannelMask))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
float3 CardPageLocalCenter;
|
|
float3 CardPageLocalExtent;
|
|
GetCardLocalBBox(CardPage, Card, UVMin, UVMax, CardTileDepthRange, CardPageLocalCenter, CardPageLocalExtent);
|
|
|
|
float3 CardPageWorldCenter = mul(Card.WorldToLocalRotation, CardPageLocalCenter) + Card.Origin;
|
|
float3 CardPageWorldExtent = mul(abs(Card.WorldToLocalRotation), CardPageLocalExtent);
|
|
float CardPageWorldBoundingSphere = length(CardPageLocalExtent);
|
|
OutCardPageWorldCenter = CardPageWorldCenter;
|
|
|
|
const uint LightType = LumenLight.Type;
|
|
const float3 LightPosition = LumenLight.ProxyPosition;
|
|
const float3 LightDirection = LumenLight.ProxyDirection;
|
|
const float LightRadius = LumenLight.ProxyRadius;
|
|
|
|
if (LightType == LIGHT_TYPE_DIRECTIONAL)
|
|
{
|
|
return true;
|
|
}
|
|
else if (LightType == LIGHT_TYPE_POINT)
|
|
{
|
|
// Point light
|
|
return true;
|
|
}
|
|
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 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 bInFrontOfPlane;
|
|
}
|
|
|
|
// Error: Unknown light type
|
|
return false;
|
|
}
|
|
|
|
RWStructuredBuffer<uint> RWLightTileAllocator;
|
|
RWStructuredBuffer<uint2> RWLightTiles;
|
|
RWStructuredBuffer<uint> RWLightTileAllocatorPerLight;
|
|
RWStructuredBuffer<uint> RWLightTileAllocatorForPerCardTileDispatch;
|
|
RWStructuredBuffer<uint> RWLightTileOffsetNumPerCardTile;
|
|
|
|
uint MaxLightsPerTile;
|
|
uint NumLights;
|
|
uint NumViews;
|
|
int bUseLightTilesPerLightType;
|
|
|
|
struct FLightSample
|
|
{
|
|
float Weight;
|
|
uint LightIndex;
|
|
uint LightType; // 0:standalone, 1:point, 2:spot, 3:rect
|
|
bool bHasShadowMask;
|
|
};
|
|
|
|
struct FLightSampleAccumulator
|
|
{
|
|
uint PackedSamples[MAX_LIGHT_SAMPLES];
|
|
};
|
|
|
|
FLightSampleAccumulator InitLightSampleAccumulator()
|
|
{
|
|
FLightSampleAccumulator LightSampleAccumulator = (FLightSampleAccumulator)0;
|
|
for (uint PackedSampleIndex = 0; PackedSampleIndex < MAX_LIGHT_SAMPLES; ++PackedSampleIndex)
|
|
{
|
|
LightSampleAccumulator.PackedSamples[PackedSampleIndex] = 0;
|
|
}
|
|
return LightSampleAccumulator;
|
|
}
|
|
|
|
uint PackLightSample(FLightSample LightSample)
|
|
{
|
|
uint PackedLightSample = LightSample.LightIndex & 0x3FFF;
|
|
PackedLightSample |= (LightSample.LightType << 14u) & 0xC000;
|
|
PackedLightSample |= LightSample.bHasShadowMask ? 0x10000 : 0;
|
|
PackedLightSample |= (asuint(LightSample.Weight) << 1u) & 0xFFFE0000;
|
|
return PackedLightSample;
|
|
}
|
|
|
|
FLightSample UnpackLightSample(uint PackedLightSample)
|
|
{
|
|
FLightSample LightSample = (FLightSample)0;
|
|
LightSample.LightIndex = PackedLightSample & 0x3FFF;
|
|
LightSample.LightType = BitFieldExtractU32(PackedLightSample, 2, 14);
|
|
LightSample.bHasShadowMask = PackedLightSample & 0x10000 ? true : false;
|
|
LightSample.Weight = 0.0f;
|
|
return LightSample;
|
|
}
|
|
|
|
void AddLightSample(inout FLightSampleAccumulator LightSampleAccumulator, FLightSample LightSample)
|
|
{
|
|
uint PackedLightSample = PackLightSample(LightSample);
|
|
|
|
// Try inserting the new light and keep things in descending order
|
|
for (uint PackedSampleIndex = 0; PackedSampleIndex < MAX_LIGHT_SAMPLES; ++PackedSampleIndex)
|
|
{
|
|
uint Temp = LightSampleAccumulator.PackedSamples[PackedSampleIndex];
|
|
if (PackedLightSample > Temp)
|
|
{
|
|
LightSampleAccumulator.PackedSamples[PackedSampleIndex] = PackedLightSample;
|
|
PackedLightSample = Temp;
|
|
#if MAX_LIGHT_SAMPLES <= 16 // workaround for a shader compiler bug
|
|
if (PackedLightSample == 0)
|
|
{
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
#ifdef BuildLightTilesCS
|
|
|
|
/**
|
|
* Pick N most important lights per tile in page selected to update to update this frame, and output a list of light tiles
|
|
*/
|
|
[numthreads(THREADGROUP_SIZE, 1, 1)]
|
|
void BuildLightTilesCS(
|
|
uint3 GroupId : SV_GroupID,
|
|
uint3 DispatchThreadId : SV_DispatchThreadID,
|
|
uint3 GroupThreadId : SV_GroupThreadID)
|
|
{
|
|
// One thread per tile
|
|
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 / CARD_TILE_SIZE;
|
|
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;
|
|
|
|
float2 CardTileDepthRange = GetCardTileDepthRange(CardTileIndex);
|
|
|
|
uint ViewIndex = GetCardViewIndex(CardPage, Card, UVMin, UVMax, CardTileDepthRange, NumViews, true);
|
|
|
|
// Loop over lights to select N most important lights
|
|
for (uint LightIndex = 0; LightIndex < NumLights; ++LightIndex)
|
|
{
|
|
if (DoesLightInfluenceSphereAffectCardPageUVRange(LoadLumenLightInfluenceSphere(LightIndex), CardPage, Card, UVMin, UVMax, CardTileDepthRange))
|
|
{
|
|
const FDFVector3 PreViewTranslation = GetPreViewTranslation(ViewIndex);
|
|
FLumenLight LumenLight = LoadLumenLight(LightIndex, DFHackToFloat(PreViewTranslation), ViewExposure[ViewIndex]);
|
|
|
|
float3 CardPageWorldCenter = 0.0f; // LWC_TODO:
|
|
bool bLightAffectsCard = DoesLightAffectCardPageUVRange(LumenLight, CardPage, Card, UVMin, UVMax, CardTileDepthRange, CardPageWorldCenter);
|
|
if (bLightAffectsCard)
|
|
{
|
|
// Center of a tile for estimating attenuation
|
|
float3 TranslatedWorldPosition = CardPageWorldCenter + DFHackToFloat(PrimaryView.PreViewTranslation);
|
|
|
|
FLightSample LightSample;
|
|
LightSample.Weight = GetLightWeight(LumenLight, TranslatedWorldPosition);
|
|
LightSample.LightIndex = LightIndex;
|
|
LightSample.LightType = LumenLight.bIsStandaloneLight ? 0 : LumenLight.Type;
|
|
LightSample.bHasShadowMask = LumenLight.bHasShadowMask;
|
|
AddLightSample(LightSampleAccumulator, LightSample);
|
|
}
|
|
}
|
|
}
|
|
|
|
uint NumPackedLightSamples = 0;
|
|
for (uint PackedSampleIndex = 0; PackedSampleIndex < MAX_LIGHT_SAMPLES; ++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]);
|
|
|
|
// Write light tile to global light tile array
|
|
FLightTileForCompactionPass LightTile;
|
|
LightTile.LightIndex = LightSample.LightIndex;
|
|
LightTile.ViewIndex = ViewIndex;
|
|
LightTile.bHasShadowMask = LightSample.bHasShadowMask;
|
|
LightTile.CardTileIndex = CardTileIndex;
|
|
LightTile.CulledLightIndex = LightSampleIndex;
|
|
LightTile.LightType = LightSample.LightType;
|
|
RWLightTiles[LightTileOffset + LightSampleIndex] = PackLightTileForCompactionPass(LightTile);
|
|
|
|
uint SlotIndex = bUseLightTilesPerLightType != 0 && LightSample.LightType > 0 ?
|
|
LightSample.LightType - 1 :
|
|
NUM_BATCHABLE_LIGHT_TYPES + LightSample.LightIndex;
|
|
InterlockedAdd(RWLightTileAllocatorPerLight[SlotIndex * NumViews + ViewIndex], 1);
|
|
}
|
|
|
|
if (NumPackedLightSamples > 0)
|
|
{
|
|
uint CardLightTilesOffset;
|
|
InterlockedAdd(RWLightTileAllocatorForPerCardTileDispatch[0], NumPackedLightSamples, CardLightTilesOffset);
|
|
PackedOffsetNum = (NumPackedLightSamples << 24) | CardLightTilesOffset;
|
|
}
|
|
}
|
|
|
|
RWLightTileOffsetNumPerCardTile[CardTileIndex] = PackedOffsetNum;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
StructuredBuffer<uint> LightTileAllocatorPerLight;
|
|
RWStructuredBuffer<uint> RWLightTileOffsetsPerLight;
|
|
|
|
#ifdef ComputeLightTileOffsetsPerLightCS
|
|
|
|
StructuredBuffer<int> StandaloneLightIndices;
|
|
uint NumStandaloneLights;
|
|
|
|
/**
|
|
* Prefix sum for card tile array compaction
|
|
*/
|
|
[numthreads(THREADGROUP_SIZE, 1, 1)]
|
|
void ComputeLightTileOffsetsPerLightCS(uint3 DispatchThreadId : SV_DispatchThreadID)
|
|
{
|
|
if (DispatchThreadId.x == 0)
|
|
{
|
|
uint TileOffset = 0;
|
|
|
|
for (uint ViewIndex = 0; ViewIndex < NumViews; ViewIndex++)
|
|
{
|
|
#if USE_STANDALONE_LIGHT_INDICES
|
|
for (uint BufferIndex = 0; BufferIndex < NumStandaloneLights; ++BufferIndex)
|
|
{
|
|
uint LightIndex = uint(StandaloneLightIndices[BufferIndex]);
|
|
#else
|
|
for (uint LightIndex = 0; LightIndex < NumLights; ++LightIndex)
|
|
{
|
|
#endif
|
|
uint Index = NUM_BATCHABLE_LIGHT_TYPES + LightIndex;
|
|
RWLightTileOffsetsPerLight[Index * NumViews + ViewIndex] = TileOffset;
|
|
TileOffset += LightTileAllocatorPerLight[Index * NumViews + ViewIndex];
|
|
}
|
|
}
|
|
|
|
for (uint ViewIndex = 0; ViewIndex < NumViews; ViewIndex++)
|
|
{
|
|
for (uint LightTypeIndex = 0; LightTypeIndex < NUM_BATCHABLE_LIGHT_TYPES; ++LightTypeIndex)
|
|
{
|
|
RWLightTileOffsetsPerLight[LightTypeIndex * NumViews + ViewIndex] = TileOffset;
|
|
TileOffset += LightTileAllocatorPerLight[LightTypeIndex * NumViews + ViewIndex];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
RWStructuredBuffer<uint2> RWCompactedLightTiles;
|
|
RWStructuredBuffer<uint2> RWLightTilesPerCardTile;
|
|
RWStructuredBuffer<uint> RWCompactedLightTileAllocatorPerLight;
|
|
StructuredBuffer<uint> LightTileAllocator;
|
|
StructuredBuffer<uint2> LightTiles;
|
|
StructuredBuffer<uint> LightTileOffsetsPerLight;
|
|
StructuredBuffer<uint> LightTileOffsetNumPerCardTile;
|
|
|
|
#ifdef CompactLightTilesCS
|
|
|
|
/**
|
|
* Compact card tile array
|
|
*/
|
|
[numthreads(THREADGROUP_SIZE, 1, 1)]
|
|
void CompactLightTilesCS(uint3 DispatchThreadId : SV_DispatchThreadID)
|
|
{
|
|
const uint LightTileIndex = DispatchThreadId.x;
|
|
|
|
if (LightTileIndex < LightTileAllocator[0])
|
|
{
|
|
FLightTileForCompactionPass LightTile = UnpackLightTileForCompactionPass(LightTiles[LightTileIndex]);
|
|
|
|
uint SlotIndex = bUseLightTilesPerLightType != 0 && LightTile.LightType > 0 ? LightTile.LightType - 1 : NUM_BATCHABLE_LIGHT_TYPES + LightTile.LightIndex;
|
|
uint CompactedLightTileIndex = 0;
|
|
InterlockedAdd(RWCompactedLightTileAllocatorPerLight[SlotIndex * NumViews + LightTile.ViewIndex], 1, CompactedLightTileIndex);
|
|
CompactedLightTileIndex += LightTileOffsetsPerLight[SlotIndex * NumViews + LightTile.ViewIndex];
|
|
|
|
uint CardTileIndex = LightTile.CardTileIndex;
|
|
FCardTileData CardTile = UnpackCardTileData(CardTiles[CardTileIndex]);
|
|
|
|
FLightTileForShadowMaskPass TileForLight;
|
|
TileForLight.LightIndex = LightTile.LightIndex;
|
|
TileForLight.ViewIndex = LightTile.ViewIndex;
|
|
TileForLight.CardPageIndex = CardTile.CardPageIndex;
|
|
TileForLight.TileCoord = CardTile.TileCoord;
|
|
uint2 PackedLightTile = PackLightTileForShadowMaskPass(TileForLight);
|
|
RWCompactedLightTiles[CompactedLightTileIndex] = PackedLightTile;
|
|
|
|
uint PackedOffsetNum = LightTileOffsetNumPerCardTile[CardTileIndex];
|
|
uint LightTileOffset = (PackedOffsetNum & 0x00ffffff) + LightTile.CulledLightIndex;
|
|
FLightTileForLightPass TileForCardTile;
|
|
TileForCardTile.LightIndex = LightTile.LightIndex;
|
|
TileForCardTile.ViewIndex = LightTile.ViewIndex;
|
|
TileForCardTile.ShadowMaskIndex = LightTile.bHasShadowMask ? CompactedLightTileIndex : 0xffffffff;
|
|
PackedLightTile = PackLightTileForLightPass(TileForCardTile);
|
|
RWLightTilesPerCardTile[LightTileOffset] = PackedLightTile;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
RWBuffer<uint> RWDispatchLightTilesIndirectArgs;
|
|
RWBuffer<uint> RWDrawTilesPerLightIndirectArgs;
|
|
RWBuffer<uint> RWDispatchTilesPerLightIndirectArgs;
|
|
|
|
uint VertexCountPerInstanceIndirect;
|
|
uint PerLightDispatchFactor;
|
|
|
|
#ifdef InitializeLightTileIndirectArgsCS
|
|
|
|
[numthreads(THREADGROUP_SIZE, 1, 1)]
|
|
void InitializeLightTileIndirectArgsCS(uint3 DispatchThreadId : SV_DispatchThreadID)
|
|
{
|
|
uint PerViewLightIndex = DispatchThreadId.x;
|
|
|
|
// Global card tile array
|
|
if (PerViewLightIndex == 0)
|
|
{
|
|
uint NumLightTiles = LightTileAllocator[0];
|
|
|
|
// NumTilesDiv1
|
|
WriteDispatchIndirectArgs(RWDispatchLightTilesIndirectArgs, 0, NumLightTiles, 1, 1);
|
|
|
|
// NumTilesDiv64
|
|
WriteDispatchIndirectArgs(RWDispatchLightTilesIndirectArgs, 1, DivideAndRoundUp64(NumLightTiles), 1, 1);
|
|
}
|
|
|
|
// Per light card tile array
|
|
if (PerViewLightIndex < NumLights * NumViews)
|
|
{
|
|
uint NumLightTilesPerLight = LightTileAllocatorPerLight[PerViewLightIndex];
|
|
|
|
// FRHIDispatchIndirectParameters
|
|
WriteDispatchIndirectArgs(RWDispatchTilesPerLightIndirectArgs, PerViewLightIndex, PerLightDispatchFactor * NumLightTilesPerLight, 1, 1);
|
|
|
|
// FRHIDrawIndirectParameters
|
|
RWDrawTilesPerLightIndirectArgs[4 * PerViewLightIndex + 0] = VertexCountPerInstanceIndirect;
|
|
RWDrawTilesPerLightIndirectArgs[4 * PerViewLightIndex + 1] = NumLightTilesPerLight;
|
|
RWDrawTilesPerLightIndirectArgs[4 * PerViewLightIndex + 2] = 0;
|
|
RWDrawTilesPerLightIndirectArgs[4 * PerViewLightIndex + 3] = 0;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|