668 lines
24 KiB
HLSL
668 lines
24 KiB
HLSL
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
/*=============================================================================
|
|
LumenCardSceneLighting.usf
|
|
=============================================================================*/
|
|
|
|
#include "../Common.ush"
|
|
#include "../BRDF.ush"
|
|
#include "LumenFloatQuantization.ush"
|
|
#include "LumenCardCommon.ush"
|
|
#include "LumenCardTile.ush"
|
|
#include "LumenCardTileShadowDownsampleFactor.ush"
|
|
#include "LumenSceneLighting.ush"
|
|
#include "SurfaceCache/LumenSurfaceCacheSampling.ush"
|
|
#include "Radiosity/LumenRadiosity.ush"
|
|
|
|
#ifndef THREADGROUP_SIZE
|
|
#define THREADGROUP_SIZE 1
|
|
#endif
|
|
|
|
RWStructuredBuffer<float4> RWCardPageBuffer;
|
|
StructuredBuffer<uint> CardPageLastUsedBuffer;
|
|
StructuredBuffer<uint> CardPageHighResLastUsedBuffer;
|
|
|
|
RWStructuredBuffer<uint> RWDirectLightingCardPageIndexAllocator;
|
|
RWStructuredBuffer<uint> RWDirectLightingCardPageIndexData;
|
|
|
|
RWStructuredBuffer<uint> RWIndirectLightingCardPageIndexAllocator;
|
|
RWStructuredBuffer<uint> RWIndirectLightingCardPageIndexData;
|
|
|
|
RWStructuredBuffer<uint> RWPriorityHistogram;
|
|
StructuredBuffer<uint> PriorityHistogram;
|
|
|
|
RWStructuredBuffer<uint> RWMaxUpdateBucket;
|
|
StructuredBuffer<uint> MaxUpdateBucket;
|
|
|
|
RWStructuredBuffer<uint> RWCardPageTileAllocator;
|
|
StructuredBuffer<uint> CardPageTileAllocator;
|
|
|
|
float MaxDistanceFromFrustumToPrioritize;
|
|
uint FreezeUpdateFrame;
|
|
uint CardPageNum;
|
|
uint MaxDirectLightingTilesToUpdate;
|
|
uint MaxIndirectLightingTilesToUpdate;
|
|
float FirstClipmapWorldExtentRcp;
|
|
uint IndirectLightingHistoryValid;
|
|
uint NumCameraOrigins;
|
|
float4 WorldCameraOrigins[LUMEN_MAX_VIEWS];
|
|
float4 CameraFrustumPlanes[LUMEN_MAX_VIEWS * LUMEN_MAX_CAMERA_PLANES];
|
|
|
|
float DirectLightingUpdateFactor;
|
|
float IndirectLightingUpdateFactor;
|
|
|
|
/**
|
|
* Batch clear all resources required for the subsequent card context update pass
|
|
*/
|
|
[numthreads(THREADGROUP_SIZE, 1, 1)]
|
|
void ClearCardUpdateContextCS(
|
|
uint3 DispatchThreadId : SV_DispatchThreadID)
|
|
{
|
|
uint ElementIndex = DispatchThreadId.x;
|
|
|
|
if (ElementIndex < 1)
|
|
{
|
|
RWDirectLightingCardPageIndexAllocator[ElementIndex] = 0;
|
|
RWIndirectLightingCardPageIndexAllocator[ElementIndex] = 0;
|
|
}
|
|
|
|
if (ElementIndex < CARD_UPDATE_CONTEXT_MAX * MAX_UPDATE_BUCKET_STRIDE)
|
|
{
|
|
RWMaxUpdateBucket[ElementIndex] = 0;
|
|
}
|
|
|
|
if (ElementIndex < CARD_UPDATE_CONTEXT_MAX * CARD_PAGE_TILE_ALLOCATOR_STRIDE)
|
|
{
|
|
RWCardPageTileAllocator[ElementIndex] = 0;
|
|
}
|
|
|
|
if (ElementIndex < CARD_UPDATE_CONTEXT_MAX * PRIORITY_HISTOGRAM_SIZE)
|
|
{
|
|
RWPriorityHistogram[ElementIndex] = 0;
|
|
}
|
|
}
|
|
|
|
uint GetMaxTilesToUpdate(uint CardUpdateContext)
|
|
{
|
|
return CardUpdateContext == CARD_UPDATE_CONTEXT_DIRECT_LIGHTING ? MaxDirectLightingTilesToUpdate : MaxIndirectLightingTilesToUpdate;
|
|
}
|
|
|
|
uint GetLastLightingUpdateFrameIndex(FLumenCardPageData CardPage, uint CardUpdateContext)
|
|
{
|
|
return CardUpdateContext == CARD_UPDATE_CONTEXT_DIRECT_LIGHTING ? CardPage.LastDirectLightingUpdateFrameIndex : CardPage.LastIndirectLightingUpdateFrameIndex;
|
|
}
|
|
|
|
uint GetUpdateFactor(uint CardUpdateContext)
|
|
{
|
|
return CardUpdateContext == CARD_UPDATE_CONTEXT_DIRECT_LIGHTING ? DirectLightingUpdateFactor : IndirectLightingUpdateFactor;
|
|
}
|
|
|
|
uint GetNumCardPageTiles(FLumenCardPageData CardPage)
|
|
{
|
|
return (CardPage.SizeInTexels.x * CardPage.SizeInTexels.y) / (CARD_TILE_SIZE * CARD_TILE_SIZE);
|
|
}
|
|
|
|
uint GetPriorityBucketIndex(FLumenCardData Card, FLumenCardPageData CardPage, uint CardPageIndex, uint CardUpdateContext)
|
|
{
|
|
uint LastLightingUpdateFrameIndex = GetLastLightingUpdateFrameIndex(CardPage, CardUpdateContext);
|
|
const float UpdateFactor = GetUpdateFactor(CardUpdateContext);
|
|
|
|
// FramesSinceLastUpdated >= 1
|
|
uint FramesSinceLastUpdated = SurfaceCacheUpdateFrameIndex - LastLightingUpdateFrameIndex;
|
|
|
|
float UpdateSpeed = 1.0f;
|
|
{
|
|
bool bNearAnyFrustum = false;
|
|
float DistanceFromViewer = 100000000.0f;
|
|
|
|
for (uint ViewIndex = 0; ViewIndex < NumCameraOrigins; ViewIndex++)
|
|
{
|
|
float3 CardSpaceViewPosition = mul(WorldCameraOrigins[ViewIndex].xyz - Card.Origin, Card.WorldToLocalRotation);
|
|
float3 CardPageLocalCenter;
|
|
float3 CardPageLocalExtent;
|
|
GetCardPageLocalBBox(CardPage, Card, CardPageLocalCenter, CardPageLocalExtent);
|
|
DistanceFromViewer = min(DistanceFromViewer, sqrt(ComputeSquaredDistanceFromBoxToPoint(CardPageLocalCenter, CardPageLocalExtent, CardSpaceViewPosition)));
|
|
|
|
// Check whether it's near frustum
|
|
float3 CardPageWorldCenter = mul(Card.WorldToLocalRotation, CardPageLocalCenter) + DFFastToTranslatedWorld(Card.Origin, GetPreViewTranslation(ViewIndex));
|
|
float3 CardPageWorldExtent = mul(abs(Card.WorldToLocalRotation), CardPageLocalExtent);
|
|
|
|
if (MaxDistanceFromFrustumToPrioritize >= 0.0f)
|
|
{
|
|
bool bNearFrustum = true;
|
|
for (uint PlaneIndex = 0; PlaneIndex < LUMEN_MAX_CAMERA_PLANES; ++PlaneIndex)
|
|
{
|
|
float DistanceFromPageCenterToPlane = dot(CameraFrustumPlanes[LUMEN_MAX_CAMERA_PLANES * ViewIndex + PlaneIndex], float4(CardPageWorldCenter, 1.0f));
|
|
float Radius = dot(CardPageWorldExtent, abs(CameraFrustumPlanes[LUMEN_MAX_CAMERA_PLANES * ViewIndex + PlaneIndex].xyz));
|
|
bNearFrustum = bNearFrustum && (DistanceFromPageCenterToPlane <= Radius + MaxDistanceFromFrustumToPrioritize);
|
|
}
|
|
|
|
if (bNearFrustum)
|
|
{
|
|
bNearAnyFrustum = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
UpdateSpeed = 1.0f / (1.0f + max(DistanceFromViewer * FirstClipmapWorldExtentRcp, 0.0f));
|
|
|
|
// Speedup updates for card pages near frustum
|
|
if (bNearAnyFrustum)
|
|
{
|
|
UpdateSpeed *= 2.0f;
|
|
}
|
|
}
|
|
|
|
// Update speed based on the feedback
|
|
#if SURFACE_CACHE_FEEDBACK
|
|
{
|
|
const uint LastUsedHighResFrameIndex = CardPageHighResLastUsedBuffer[CardPageIndex];
|
|
|
|
// Override update rate for cards used recently
|
|
if (LastUsedHighResFrameIndex + 2 >= SurfaceCacheUpdateFrameIndex && LastUsedHighResFrameIndex <= SurfaceCacheUpdateFrameIndex)
|
|
{
|
|
UpdateSpeed = 2.0f;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (LastLightingUpdateFrameIndex == 0)
|
|
{
|
|
// Page wasn't ever updated
|
|
FramesSinceLastUpdated = 2048;
|
|
}
|
|
|
|
// Map desired page update speed to buckets and place most important pages in bucket 0
|
|
uint BucketIndex = PRIORITY_HISTOGRAM_SIZE - 1 - clamp(log2(max(4.0f * FramesSinceLastUpdated * UpdateSpeed, 1.0f)), 0, PRIORITY_HISTOGRAM_SIZE - 1);
|
|
return BucketIndex;
|
|
}
|
|
|
|
void BuildUpdatePriorityHistogram(FLumenCardData Card, FLumenCardPageData CardPage, uint CardPageIndex, uint NumCardPageTiles, uint CardUpdateContext)
|
|
{
|
|
uint PriorityBucketIndex = GetPriorityBucketIndex(Card, CardPage, CardPageIndex, CardUpdateContext);
|
|
InterlockedAdd(RWPriorityHistogram[CardUpdateContext * PRIORITY_HISTOGRAM_SIZE + PriorityBucketIndex], NumCardPageTiles);
|
|
}
|
|
|
|
/**
|
|
* Iterate over all pages and build a histogram of card update priorities
|
|
*/
|
|
[numthreads(THREADGROUP_SIZE, 1, 1)]
|
|
void BuildPageUpdatePriorityHistogramCS(
|
|
uint3 DispatchThreadId : SV_DispatchThreadID)
|
|
{
|
|
uint IndexInIndexBuffer = DispatchThreadId.x;
|
|
|
|
if (IndexInIndexBuffer < CardPageNum)
|
|
{
|
|
uint CardPageIndex = IndexInIndexBuffer;
|
|
FLumenCardPageData CardPage = GetLumenCardPageData(CardPageIndex);
|
|
FLumenCardData Card = GetLumenCardData(CardPage.CardIndex);
|
|
const uint NumCardPageTiles = GetNumCardPageTiles(CardPage);
|
|
|
|
if (NumCardPageTiles > 0)
|
|
{
|
|
BuildUpdatePriorityHistogram(Card, CardPage, CardPageIndex, NumCardPageTiles, CARD_UPDATE_CONTEXT_DIRECT_LIGHTING);
|
|
BuildUpdatePriorityHistogram(Card, CardPage, CardPageIndex, NumCardPageTiles, CARD_UPDATE_CONTEXT_INDIRECT_LIGHTING);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SelectMaxUpdateBucket(uint CardUpdateContext)
|
|
{
|
|
const uint MaxTilesToUpdate = GetMaxTilesToUpdate(CardUpdateContext);
|
|
|
|
uint UpdateTileSum = 0;
|
|
uint PriorityBucketIndex = 0;
|
|
uint PriorityBucketMaxTiles = MaxTilesToUpdate;
|
|
|
|
for (; PriorityBucketIndex < PRIORITY_HISTOGRAM_SIZE; ++PriorityBucketIndex)
|
|
{
|
|
uint TilesPerBucket = PriorityHistogram[CardUpdateContext * PRIORITY_HISTOGRAM_SIZE + PriorityBucketIndex];
|
|
|
|
if (UpdateTileSum + TilesPerBucket >= MaxTilesToUpdate)
|
|
{
|
|
PriorityBucketMaxTiles = MaxTilesToUpdate - UpdateTileSum;
|
|
break;
|
|
}
|
|
|
|
UpdateTileSum += TilesPerBucket;
|
|
}
|
|
|
|
RWMaxUpdateBucket[MAX_UPDATE_BUCKET_STRIDE * CardUpdateContext + 0] = PriorityBucketIndex;
|
|
RWMaxUpdateBucket[MAX_UPDATE_BUCKET_STRIDE * CardUpdateContext + 1] = PriorityBucketMaxTiles;
|
|
}
|
|
|
|
/**
|
|
* Compute max bucket histogram to update and how many tiles should be updated in that last bucket
|
|
*/
|
|
[numthreads(THREADGROUP_SIZE, 1, 1)]
|
|
void SelectMaxUpdateBucketCS(
|
|
uint3 GroupId : SV_GroupID,
|
|
uint3 GroupThreadId : SV_GroupThreadID,
|
|
uint3 DispatchThreadId : SV_DispatchThreadID)
|
|
{
|
|
if (GroupId.x == 0 && GroupThreadId.x == 0)
|
|
{
|
|
SelectMaxUpdateBucket(CARD_UPDATE_CONTEXT_DIRECT_LIGHTING);
|
|
}
|
|
else if (GroupId.x == 1 && GroupThreadId.x == 0)
|
|
{
|
|
SelectMaxUpdateBucket(CARD_UPDATE_CONTEXT_INDIRECT_LIGHTING);
|
|
}
|
|
}
|
|
|
|
bool BuildCardsUpdateList(
|
|
FLumenCardData Card,
|
|
FLumenCardPageData CardPage,
|
|
uint CardPageIndex,
|
|
uint NumCardPageTiles,
|
|
uint CardUpdateContext,
|
|
RWStructuredBuffer<uint> RWCardPageIndexAllocator,
|
|
RWStructuredBuffer<uint> RWCardPageIndexData)
|
|
{
|
|
const uint MaxTilesToUpdate = GetMaxTilesToUpdate(CardUpdateContext);
|
|
const uint MaxUpdateBucketIndex = MaxUpdateBucket[MAX_UPDATE_BUCKET_STRIDE * CardUpdateContext + 0];
|
|
const uint MaxUpdateBucketMaxTiles = MaxUpdateBucket[MAX_UPDATE_BUCKET_STRIDE * CardUpdateContext + 1];
|
|
|
|
// Update everything up to the max selected priority bucket
|
|
uint PriorityBucketIndex = GetPriorityBucketIndex(Card, CardPage, CardPageIndex, CardUpdateContext);
|
|
bool bUpdateThisPage = PriorityBucketIndex <= MaxUpdateBucketIndex;
|
|
|
|
if (bUpdateThisPage && PriorityBucketIndex == MaxUpdateBucketIndex)
|
|
{
|
|
// Can't update more than MaxUpdateBucketMaxTiles in the max bucket to preserve the general order
|
|
uint NumAllocatedTilesInMaxUpdateBucket = 0;
|
|
InterlockedAdd(RWCardPageTileAllocator[CARD_PAGE_TILE_ALLOCATOR_STRIDE * CardUpdateContext + 1], NumCardPageTiles, NumAllocatedTilesInMaxUpdateBucket);
|
|
|
|
if (!(NumAllocatedTilesInMaxUpdateBucket + NumCardPageTiles <= MaxUpdateBucketMaxTiles))
|
|
{
|
|
bUpdateThisPage = false;
|
|
}
|
|
}
|
|
|
|
if (bUpdateThisPage)
|
|
{
|
|
bUpdateThisPage = false;
|
|
uint NumAllocatedTiles = 0;
|
|
InterlockedAdd(RWCardPageTileAllocator[CARD_PAGE_TILE_ALLOCATOR_STRIDE * CardUpdateContext + 0], NumCardPageTiles, NumAllocatedTiles);
|
|
|
|
if (NumAllocatedTiles + NumCardPageTiles <= MaxTilesToUpdate)
|
|
{
|
|
uint NextIndex = 0;
|
|
InterlockedAdd(RWCardPageIndexAllocator[0], 1, NextIndex);
|
|
|
|
if (NextIndex < CardPageNum)
|
|
{
|
|
RWCardPageIndexData[NextIndex] = CardPageIndex;
|
|
bUpdateThisPage = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return bUpdateThisPage;
|
|
}
|
|
|
|
/**
|
|
* Iterate over all cards and pick first N for update based on the histogram max update bucket
|
|
*/
|
|
[numthreads(THREADGROUP_SIZE, 1, 1)]
|
|
void BuildCardsUpdateListCS(
|
|
uint3 DispatchThreadId : SV_DispatchThreadID)
|
|
{
|
|
uint IndexInIndexBuffer = DispatchThreadId.x;
|
|
|
|
if (IndexInIndexBuffer < CardPageNum)
|
|
{
|
|
const uint CardPageIndex = IndexInIndexBuffer;
|
|
FLumenCardPageData CardPage = GetLumenCardPageData(CardPageIndex);
|
|
FLumenCardData Card = GetLumenCardData(CardPage.CardIndex);
|
|
const uint NumCardPageTiles = GetNumCardPageTiles(CardPage);
|
|
|
|
if (NumCardPageTiles > 0)
|
|
{
|
|
bool bUpdatedCardPage = false;
|
|
|
|
// On radiosity atlas reset invalidate all history data
|
|
if (IndirectLightingHistoryValid == 0)
|
|
{
|
|
CardPage.LastIndirectLightingUpdateFrameIndex = 0;
|
|
bUpdatedCardPage = true;
|
|
}
|
|
|
|
if (BuildCardsUpdateList(
|
|
Card,
|
|
CardPage,
|
|
CardPageIndex,
|
|
NumCardPageTiles,
|
|
CARD_UPDATE_CONTEXT_DIRECT_LIGHTING,
|
|
RWDirectLightingCardPageIndexAllocator,
|
|
RWDirectLightingCardPageIndexData))
|
|
{
|
|
CardPage.LastDirectLightingUpdateFrameIndex = SurfaceCacheUpdateFrameIndex;
|
|
CardPage.DirectLightingTemporalIndex += 1;
|
|
bUpdatedCardPage = true;
|
|
}
|
|
|
|
if (BuildCardsUpdateList(
|
|
Card,
|
|
CardPage,
|
|
CardPageIndex,
|
|
NumCardPageTiles,
|
|
CARD_UPDATE_CONTEXT_INDIRECT_LIGHTING,
|
|
RWIndirectLightingCardPageIndexAllocator,
|
|
RWIndirectLightingCardPageIndexData))
|
|
{
|
|
CardPage.LastIndirectLightingUpdateFrameIndex = SurfaceCacheUpdateFrameIndex;
|
|
CardPage.IndirectLightingTemporalIndex = CardPage.IndirectLightingTemporalIndex + 1;
|
|
bUpdatedCardPage = true;
|
|
}
|
|
|
|
if (bUpdatedCardPage && FreezeUpdateFrame == 0)
|
|
{
|
|
SetCardPageUpdateData(CardPageIndex, CardPage);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
RWBuffer<uint> RWDirectLightingDrawCardPageIndicesIndirectArgs;
|
|
RWBuffer<uint> RWDirectLightingDispatchCardPageIndicesIndirectArgs;
|
|
RWBuffer<uint> RWIndirectLightingDrawCardPageIndicesIndirectArgs;
|
|
RWBuffer<uint> RWIndirectLightingDispatchCardPageIndicesIndirectArgs;
|
|
|
|
StructuredBuffer<uint> DirectLightingCardPageIndexAllocator;
|
|
StructuredBuffer<uint> IndirectLightingCardPageIndexAllocator;
|
|
uint VertexCountPerInstanceIndirect;
|
|
|
|
[numthreads(THREADGROUP_SIZE, 1, 1)]
|
|
void SetCardPageIndexIndirectArgsCS(uint3 DispatchThreadId : SV_DispatchThreadID)
|
|
{
|
|
if (DispatchThreadId.x == 0)
|
|
{
|
|
{
|
|
uint NumPageIndices = DirectLightingCardPageIndexAllocator[0];
|
|
|
|
// FRHIDrawIndirectParameters
|
|
RWDirectLightingDrawCardPageIndicesIndirectArgs[0] = VertexCountPerInstanceIndirect;
|
|
RWDirectLightingDrawCardPageIndicesIndirectArgs[1] = NumPageIndices;
|
|
RWDirectLightingDrawCardPageIndicesIndirectArgs[2] = 0;
|
|
RWDirectLightingDrawCardPageIndicesIndirectArgs[3] = 0;
|
|
|
|
// Thread per page
|
|
WriteDispatchIndirectArgs(RWDirectLightingDispatchCardPageIndicesIndirectArgs, 0, DivideAndRoundUp64(NumPageIndices), 1, 1);
|
|
|
|
// Thread per tile
|
|
WriteDispatchIndirectArgs(RWDirectLightingDispatchCardPageIndicesIndirectArgs, 1, 4 * NumPageIndices, 1, 1);
|
|
}
|
|
|
|
{
|
|
uint NumPageIndices = IndirectLightingCardPageIndexAllocator[0];
|
|
|
|
// FRHIDrawIndirectParameters
|
|
RWIndirectLightingDrawCardPageIndicesIndirectArgs[0] = VertexCountPerInstanceIndirect;
|
|
RWIndirectLightingDrawCardPageIndicesIndirectArgs[1] = NumPageIndices;
|
|
RWIndirectLightingDrawCardPageIndicesIndirectArgs[2] = 0;
|
|
RWIndirectLightingDrawCardPageIndicesIndirectArgs[3] = 0;
|
|
|
|
// Thread per page
|
|
WriteDispatchIndirectArgs(RWIndirectLightingDispatchCardPageIndicesIndirectArgs, 0, DivideAndRoundUp64(NumPageIndices), 1, 1);
|
|
|
|
// Thread per tile
|
|
WriteDispatchIndirectArgs(RWIndirectLightingDispatchCardPageIndicesIndirectArgs, 1, 4 * NumPageIndices, 1, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
RWStructuredBuffer<uint> RWQuadAllocator;
|
|
RWStructuredBuffer<uint> RWQuadData;
|
|
|
|
StructuredBuffer<uint> CardPageIndexAllocator;
|
|
StructuredBuffer<uint> CardPageIndexData;
|
|
|
|
float2 IndirectLightingAtlasSize;
|
|
|
|
void RasterizeToCardsVS(
|
|
uint VertexId : SV_VertexID,
|
|
uint InstanceId : SV_InstanceID,
|
|
out FCardVSToPS CardInterpolants,
|
|
out float4 OutPosition : SV_POSITION
|
|
)
|
|
{
|
|
float2 TexCoord = float2(0.0f, 0.0f);
|
|
TexCoord.x += VertexId == 1 || VertexId == 2 || VertexId == 4 ? 1.0f : 0.0f;
|
|
TexCoord.y += VertexId == 2 || VertexId == 4 || VertexId == 5 ? 1.0f : 0.0f;
|
|
|
|
uint QuadIndex = InstanceId.x;
|
|
|
|
CardInterpolants = (FCardVSToPS)0;
|
|
OutPosition = 0;
|
|
|
|
if (QuadIndex < CardPageIndexAllocator[0])
|
|
{
|
|
uint CardPageIndex = CardPageIndexData[QuadIndex];
|
|
FLumenCardPageData CardPage = GetLumenCardPageData(CardPageIndex);
|
|
FLumenCardData Card = GetLumenCardData(CardPage.CardIndex);
|
|
|
|
float2 ScreenUV = lerp(CardPage.PhysicalAtlasUVRect.xy, CardPage.PhysicalAtlasUVRect.zw, TexCoord);
|
|
float2 AtlasUV = ScreenUV;
|
|
|
|
#if RADIOSITY_ATLAS_DOWNSAMPLE_FACTOR == 1
|
|
float2 IndirectLightingAtlasUV = AtlasUV;
|
|
#else
|
|
// When sampling from a downsampled Indirect Lighting atlas we need to appropriately clamp input UVs to prevent bilinear reading outside of the valid area
|
|
float2 CardWidthInTexels = (CardPage.PhysicalAtlasUVRect.zw - CardPage.PhysicalAtlasUVRect.xy) * IndirectLightingAtlasSize;
|
|
float2 ClampBorder = 0.5f / CardWidthInTexels;
|
|
float2 IndirectLightingTexCoord = clamp(TexCoord, ClampBorder, 1.0f - ClampBorder);
|
|
float2 IndirectLightingAtlasUV = lerp(CardPage.PhysicalAtlasUVRect.xy, CardPage.PhysicalAtlasUVRect.zw, IndirectLightingTexCoord);
|
|
#endif
|
|
|
|
float2 ScreenPosition = float2(2.0f, -2.0f) * ScreenUV + float2(-1.0f, 1.0f);
|
|
OutPosition = float4(ScreenPosition, 0, 1);
|
|
|
|
float2 QuadCorner = -2.0f * TexCoord + 1.0f;
|
|
CardInterpolants.AtlasUV = AtlasUV;
|
|
CardInterpolants.IndirectLightingAtlasUV = IndirectLightingAtlasUV;
|
|
CardInterpolants.CardUV = lerp(CardPage.CardUVRect.xy, CardPage.CardUVRect.zw, TexCoord);
|
|
CardInterpolants.CardTileIndex = 0;
|
|
CardInterpolants.CardPageIndex = CardPageIndex;
|
|
}
|
|
}
|
|
|
|
SamplerState BilinearClampedSampler;
|
|
StructuredBuffer<uint> CardTiles;
|
|
RWTexture2D<float3> RWFinalLightingAtlas;
|
|
float2 IndirectLightingAtlasHalfTexelSize;
|
|
|
|
[numthreads(CARD_TILE_SIZE, CARD_TILE_SIZE, 1)]
|
|
void CombineLumenSceneLightingCS(
|
|
uint3 GroupId : SV_GroupID,
|
|
uint3 GroupThreadId : SV_GroupThreadID)
|
|
{
|
|
uint CardTileIndex = GroupId.x;
|
|
uint2 TexelCoordInTile = GroupThreadId.xy;
|
|
|
|
FCardTileData CardTile = UnpackCardTileData(CardTiles[CardTileIndex]);
|
|
|
|
FLumenCardPageData CardPage = GetLumenCardPageData(CardTile.CardPageIndex);
|
|
FLumenCardData Card = GetLumenCardData(CardPage.CardIndex);
|
|
|
|
uint2 CoordInCardPage = CARD_TILE_SIZE * CardTile.TileCoord + TexelCoordInTile;
|
|
uint2 AtlasCoord = CardPage.PhysicalAtlasCoord + CoordInCardPage;
|
|
float2 AtlasUV = CardPage.PhysicalAtlasUVRect.xy + CardPage.PhysicalAtlasUVTexelScale * (CoordInCardPage + 0.5);
|
|
|
|
#if RADIOSITY_ATLAS_DOWNSAMPLE_FACTOR == 1
|
|
float2 IndirectLightingAtlasUV = AtlasUV;
|
|
#else
|
|
// When sampling from a downsampled Indirect Lighting atlas we need to appropriately clamp input UVs to prevent bilinear reading outside of the valid area
|
|
float2 IndirectLightingAtlasUV = clamp(AtlasUV, CardPage.PhysicalAtlasUVRect.xy + IndirectLightingAtlasHalfTexelSize, CardPage.PhysicalAtlasUVRect.zw - IndirectLightingAtlasHalfTexelSize);
|
|
#endif
|
|
|
|
float3 Albedo = Texture2DSampleLevel(AlbedoAtlas, BilinearClampedSampler, AtlasUV, 0).xyz;
|
|
float3 Emissive = Texture2DSampleLevel(EmissiveAtlas, BilinearClampedSampler, AtlasUV, 0).xyz;
|
|
float3 DirectLighting = Texture2DSampleLevel(DirectLightingAtlas, BilinearClampedSampler, AtlasUV, 0).xyz;
|
|
float3 IndirectLighting = Texture2DSampleLevel(IndirectLightingAtlas, BilinearClampedSampler, IndirectLightingAtlasUV, 0).xyz;
|
|
|
|
float3 FinalLighting = CombineFinalLighting(Albedo, Emissive, DirectLighting, IndirectLighting);
|
|
RWFinalLightingAtlas[AtlasCoord] = QuantizeForFloatRenderTarget(FinalLighting, int3(AtlasCoord, View.StateFrameIndexMod8 + 2));
|
|
}
|
|
|
|
void ClearLumenCardsPS(
|
|
out float4 Target0 : SV_Target0
|
|
#if NUM_TARGETS > 1
|
|
,out float4 Target1 : SV_Target1
|
|
#endif
|
|
)
|
|
{
|
|
Target0 = float4(0.0f, 0.0f, 0.0f, 0.0f);
|
|
|
|
#if NUM_TARGETS > 1
|
|
Target1 = float4(0.0f, 0.0f, 0.0f, 0.0f);
|
|
#endif
|
|
}
|
|
|
|
void ClearLumenCardCapturePS(
|
|
out float4 OutAlbedo : SV_Target0,
|
|
out float4 OutNormals : SV_Target1,
|
|
out float4 OutEmissive : SV_Target2
|
|
)
|
|
{
|
|
OutAlbedo = float4(0.0f, 0.0f, 0.0f, 0.0f);
|
|
OutNormals = float4(0.5f, 0.5f, 0.0f, 0.0f);
|
|
OutEmissive = float4(0.0f, 0.0f, 0.0f, 0.0f);
|
|
}
|
|
|
|
#ifdef ResampleLightingHistoryToCardCaptureAtlasCS
|
|
|
|
#define RESAMPLE_TILE_SHADOW_DOWNSAMPLE_FACTOR (FEATURE_LEVEL >= FEATURE_LEVEL_SM6 || PLATFORM_SUPPORTS_SM6_0_WAVE_OPERATIONS)
|
|
|
|
Buffer<uint> NewCardTileResampleData;
|
|
Buffer<uint4> NewCardPageResampleData;
|
|
Buffer<uint4> RectCoordBuffer;
|
|
Texture2D RadiosityNumFramesAccumulatedAtlas;
|
|
|
|
RWTexture2D<float4> RWDirectLightingCardCaptureAtlas;
|
|
RWTexture2D<float4> RWRadiosityCardCaptureAtlas;
|
|
RWTexture2D<UNORM float> RWRadiosityNumFramesAccumulatedCardCaptureAtlas;
|
|
|
|
uint CardCaptureAtlasWidthInTiles;
|
|
|
|
[numthreads(CARD_TILE_SIZE, CARD_TILE_SIZE, 1)]
|
|
void ResampleLightingHistoryToCardCaptureAtlasCS(uint3 GroupId : SV_GroupID, uint3 GroupThreadId : SV_GroupThreadID, uint GroupThreadIndex : SV_GroupIndex)
|
|
{
|
|
const uint TileIndex = GroupId.x;
|
|
const uint PackedTileData = NewCardTileResampleData[TileIndex];
|
|
const uint2 TileCoordInRect = uint2(BitFieldExtractU32(PackedTileData, 4, 0), BitFieldExtractU32(PackedTileData, 4, 4));
|
|
const uint RectIndex = BitFieldExtractU32(PackedTileData, 24, 8);
|
|
const uint4 RectCoord = RectCoordBuffer[RectIndex];
|
|
|
|
const uint2 TexelCoordInRect = TileCoordInRect * CARD_TILE_SIZE + GroupThreadId.xy;
|
|
const uint2 RectSize = RectCoord.zw - RectCoord.xy;
|
|
const float2 RectUV = (TexelCoordInRect + float2(0.5, 0.5)) / RectSize;
|
|
|
|
float3 OutDirectLightingCardCaptureAtlas = 0;
|
|
float3 OutRadiosityCardCaptureAtlas = 0;
|
|
float OutRadiosityNumFramesAccumulatedCardCaptureAtlas = 0;
|
|
uint4 ShadowDownsampleFactorLow = 0;
|
|
uint4 ShadowDownsampleFactorHigh = 0;
|
|
|
|
uint CardIndex = NewCardPageResampleData[RectIndex * 2 + 0].x;
|
|
|
|
if (CardIndex != LUMEN_INVALID_CARD_INDEX)
|
|
{
|
|
// Load new card attributes manually
|
|
float4 NewCardUVRect = asfloat(NewCardPageResampleData[RectIndex * 2 + 1]);
|
|
float2 NewCardUV = RectUV * (NewCardUVRect.zw - NewCardUVRect.xy) + NewCardUVRect.xy;
|
|
|
|
// LumenCardScene contains the old card structure during the resample
|
|
FLumenCardData OldCard = GetLumenCardData(CardIndex);
|
|
|
|
// Assuming card extent hasn't changed
|
|
float2 LocalSamplePosition = GetCardLocalPosition(OldCard.LocalExtent, NewCardUV, 0.0f).xy;
|
|
|
|
FLumenCardSample CardSample = ComputeSurfaceCacheSample(OldCard, CardIndex, LocalSamplePosition, 0.0f, true);
|
|
if (CardSample.bValid)
|
|
{
|
|
OutDirectLightingCardCaptureAtlas = Texture2DSampleLevel(DirectLightingAtlas, GlobalBilinearClampedSampler, CardSample.PhysicalAtlasUV, 0.0f).xyz;
|
|
OutRadiosityCardCaptureAtlas = Texture2DSampleLevel(IndirectLightingAtlas, GlobalBilinearClampedSampler, CardSample.IndirectLightingPhysicalAtlasUV, 0.0f).xyz;
|
|
OutRadiosityNumFramesAccumulatedCardCaptureAtlas = Texture2DSampleLevel(RadiosityNumFramesAccumulatedAtlas, GlobalBilinearClampedSampler, CardSample.IndirectLightingPhysicalAtlasUV, 0.0f).x;
|
|
|
|
#if RESAMPLE_TILE_SHADOW_DOWNSAMPLE_FACTOR
|
|
const uint2 TileCoord = CardSample.PhysicalAtlasUV * LumenCardScene.PhysicalAtlasSize / CARD_TILE_SIZE;
|
|
const uint PhysicalAtlasWidthInTiles = LumenCardScene.PhysicalAtlasSize.x / CARD_TILE_SIZE;
|
|
LoadLumenCardTileShadowDownsampleFactor(ShadowDownsampleFactorLow, ShadowDownsampleFactorHigh, TileCoord, PhysicalAtlasWidthInTiles);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
const uint2 WriteTexelCoord = RectCoord.xy + TexelCoordInRect;
|
|
const uint2 WriteTileCoord = WriteTexelCoord / CARD_TILE_SIZE;
|
|
|
|
RWDirectLightingCardCaptureAtlas[WriteTexelCoord] = float4(OutDirectLightingCardCaptureAtlas, 0.f);
|
|
RWRadiosityCardCaptureAtlas[WriteTexelCoord] = float4(OutRadiosityCardCaptureAtlas, 0.f);
|
|
RWRadiosityNumFramesAccumulatedCardCaptureAtlas[WriteTexelCoord] = OutRadiosityNumFramesAccumulatedCardCaptureAtlas;
|
|
|
|
#if RESAMPLE_TILE_SHADOW_DOWNSAMPLE_FACTOR
|
|
ShadowDownsampleFactorLow = WaveActiveBitAnd(ShadowDownsampleFactorLow);
|
|
ShadowDownsampleFactorHigh = WaveActiveBitAnd(ShadowDownsampleFactorHigh);
|
|
#endif
|
|
|
|
if (GroupThreadIndex == 0)
|
|
{
|
|
WriteLumenCardTileShadowDownsampleFactor(ShadowDownsampleFactorLow, ShadowDownsampleFactorHigh, WriteTileCoord, CardCaptureAtlasWidthInTiles);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
Texture2D AlbedoCardCaptureAtlas;
|
|
Texture2D EmissiveCardCaptureAtlas;
|
|
Texture2D DirectLightingCardCaptureAtlas;
|
|
Texture2D RadiosityCardCaptureAtlas;
|
|
Texture2D RadiosityNumFramesAccumulatedCardCaptureAtlas;
|
|
uint2 CardCaptureAtlasSizeInTiles;
|
|
uint OutputAtlasWidthInTiles;
|
|
|
|
void CopyCardCaptureLightingToAtlasPS(
|
|
float4 SvPosition : SV_POSITION,
|
|
float2 AtlasUV : TEXCOORD0,
|
|
out float3 OutDirectLightingAtlas : SV_Target0,
|
|
out float3 OutFinalLightingAtlas : SV_Target1
|
|
#if INDIRECT_LIGHTING
|
|
, out float3 OutRadiosityAtlas : SV_Target2
|
|
, out float OutRadiosityNumFramesAccumulatedAtlas : SV_Target3
|
|
#endif
|
|
)
|
|
{
|
|
float3 Albedo = 0.0f;
|
|
float3 Emissive = 0.0f;
|
|
float3 DirectLighting = 0.0f;
|
|
float3 IndirectLighting = 0.0f;
|
|
float RadiosityNumFramesAccumulated = 0.0f;
|
|
uint4 ShadowDownsampleFactorLow = 0;
|
|
uint4 ShadowDownsampleFactorHigh = 0;
|
|
|
|
#if RESAMPLE
|
|
{
|
|
Albedo = Texture2DSampleLevel(AlbedoCardCaptureAtlas, GlobalPointClampedSampler, AtlasUV, 0).xyz;
|
|
Emissive = Texture2DSampleLevel(EmissiveCardCaptureAtlas, GlobalPointClampedSampler, AtlasUV, 0).xyz;
|
|
DirectLighting = Texture2DSampleLevel(DirectLightingCardCaptureAtlas, GlobalPointClampedSampler, AtlasUV, 0).xyz;
|
|
IndirectLighting = Texture2DSampleLevel(RadiosityCardCaptureAtlas, GlobalPointClampedSampler, AtlasUV, 0).xyz;
|
|
RadiosityNumFramesAccumulated = Texture2DSampleLevel(RadiosityNumFramesAccumulatedCardCaptureAtlas, GlobalPointClampedSampler, AtlasUV, 0).x;
|
|
|
|
const uint2 TileCoord = AtlasUV * CardCaptureAtlasSizeInTiles;
|
|
LoadLumenCardTileShadowDownsampleFactor(ShadowDownsampleFactorLow, ShadowDownsampleFactorHigh, TileCoord, CardCaptureAtlasSizeInTiles.x);
|
|
}
|
|
#endif
|
|
|
|
OutDirectLightingAtlas = DirectLighting;
|
|
OutFinalLightingAtlas = CombineFinalLighting(Albedo, Emissive, DirectLighting, IndirectLighting);
|
|
|
|
#if INDIRECT_LIGHTING
|
|
{
|
|
OutRadiosityAtlas = IndirectLighting;
|
|
OutRadiosityNumFramesAccumulatedAtlas = RadiosityNumFramesAccumulated;
|
|
}
|
|
#endif
|
|
|
|
WriteLumenCardTileShadowDownsampleFactor(ShadowDownsampleFactorLow, ShadowDownsampleFactorHigh, SvPosition.xy / CARD_TILE_SIZE, OutputAtlasWidthInTiles);
|
|
} |