525 lines
19 KiB
HLSL
525 lines
19 KiB
HLSL
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "../Common.ush"
|
|
#include "LumenMaterial.ush"
|
|
|
|
#include "../SceneTextureParameters.ush"
|
|
#include "../BRDF.ush"
|
|
#include "../Random.ush"
|
|
#include "LumenReflectionCommon.ush"
|
|
#include "../ClearCoatCommon.ush"
|
|
#include "../FastMath.ush"
|
|
#include "LumenRadianceCacheCommon.ush"
|
|
#include "LumenScreenProbeTileClassication.ush"
|
|
|
|
#ifndef THREADGROUP_SIZE
|
|
#define THREADGROUP_SIZE 1
|
|
#endif
|
|
|
|
#ifndef FRONT_LAYER_TRANSLUCENCY
|
|
#define FRONT_LAYER_TRANSLUCENCY 0
|
|
#endif
|
|
|
|
RWBuffer<uint> RWReflectionClearTileIndirectArgs;
|
|
RWBuffer<uint> RWReflectionResolveTileIndirectArgs;
|
|
RWBuffer<uint> RWReflectionClearUnusedTracingTileIndirectArgs;
|
|
RWBuffer<uint> RWReflectionTracingTileIndirectArgs;
|
|
|
|
[numthreads(1, 1, 1)]
|
|
void InitReflectionIndirectArgsCS(
|
|
uint2 GroupId : SV_GroupID,
|
|
uint3 DispatchThreadId : SV_DispatchThreadID,
|
|
uint2 GroupThreadId : SV_GroupThreadID)
|
|
{
|
|
if (all(DispatchThreadId == 0))
|
|
{
|
|
const uint3 ClearValue = uint3(0, 1, 1);
|
|
WriteDispatchIndirectArgs(RWReflectionClearTileIndirectArgs, 0, ClearValue);
|
|
WriteDispatchIndirectArgs(RWReflectionResolveTileIndirectArgs, 0, ClearValue);
|
|
WriteDispatchIndirectArgs(RWReflectionClearUnusedTracingTileIndirectArgs, 0, ClearValue);
|
|
WriteDispatchIndirectArgs(RWReflectionTracingTileIndirectArgs, 0, ClearValue);
|
|
}
|
|
}
|
|
|
|
RWTexture2DArray<float> RWDownsampledDepth;
|
|
RWTexture2D<float> RWDownsampledClosureIndex;
|
|
|
|
// Must match cpp GReflectionResolveTileSize
|
|
#define RESOLVE_TILE_SIZE 8
|
|
|
|
#ifndef PERMUTATION_OVERFLOW_TILE
|
|
#define PERMUTATION_OVERFLOW_TILE 0
|
|
#endif
|
|
|
|
RWBuffer<uint> RWReflectionClearTileData;
|
|
RWBuffer<uint> RWReflectionTileIndirectArgs;
|
|
RWBuffer<uint> RWReflectionTileData;
|
|
Texture2DArray<uint> LumenTileBitmask;
|
|
|
|
uint2 TileViewportMin;
|
|
uint2 TileViewportDimensions;
|
|
uint2 ResolveTileViewportMin;
|
|
uint2 ResolveTileViewportDimensions;
|
|
|
|
groupshared uint SharedNumTiles;
|
|
groupshared uint SharedNumClearTiles;
|
|
groupshared uint SharedTileData[THREADGROUP_SIZE * THREADGROUP_SIZE];
|
|
groupshared uint SharedTileUsed[THREADGROUP_SIZE * THREADGROUP_SIZE];
|
|
groupshared uint SharedGlobalTileOffset;
|
|
groupshared uint SharedGlobalClearTileOffset;
|
|
|
|
[numthreads(THREADGROUP_SIZE, THREADGROUP_SIZE, 1)]
|
|
void ReflectionTileClassificationBuildListsCS(
|
|
uint3 GroupId : SV_GroupID,
|
|
uint2 GroupThreadId : SV_GroupThreadID)
|
|
{
|
|
const uint ThreadIndex = GroupThreadId.y * THREADGROUP_SIZE + GroupThreadId.x;
|
|
|
|
// When generating downsampled trace tiles we need to downsample LumenTileBitmask to shared memory first
|
|
#if SUPPORT_DOWNSAMPLE_FACTOR
|
|
SharedTileUsed[ThreadIndex] = 0;
|
|
|
|
GroupMemoryBarrierWithGroupSync();
|
|
|
|
uint TileUsed = 0;
|
|
uint2 TileCoordinate = GroupId.xy * THREADGROUP_SIZE + GroupThreadId;
|
|
uint ClosureIndex = SUBSTRATE_GBUFFER_FORMAT == 1 && SUBSTRATE_MATERIAL_CLOSURE_COUNT > 1 ? GroupId.z : 0;
|
|
|
|
// Gather whether any of the resolve tiles corresponding to this tracing tile were used
|
|
for (uint Y = 0; Y < ReflectionDownsampleFactorXY.y; Y++)
|
|
{
|
|
for (uint X = 0; X < ReflectionDownsampleFactorXY.x; X++)
|
|
{
|
|
uint2 ResolveTileCoordinate = TileCoordinate * ReflectionDownsampleFactorXY + uint2(X, Y) + ResolveTileViewportMin;
|
|
|
|
if (all(ResolveTileCoordinate < ResolveTileViewportDimensions + ResolveTileViewportMin))
|
|
{
|
|
if (LumenTileBitmask[uint3(ResolveTileCoordinate, ClosureIndex)] & LUMEN_TILE_BITMASK_REFLECTIONS)
|
|
{
|
|
TileUsed = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SharedTileUsed[ThreadIndex] = TileUsed;
|
|
|
|
GroupMemoryBarrierWithGroupSync();
|
|
#endif
|
|
|
|
|
|
#if WAVE_OPS
|
|
const uint2 ThreadOffset = ZOrder2D(ThreadIndex, log2(THREADGROUP_SIZE));
|
|
|
|
#if PERMUTATION_OVERFLOW_TILE && SUBSTRATE_GBUFFER_FORMAT==1
|
|
const uint LinearIndex = GroupId.x * THREADGROUP_SIZE * THREADGROUP_SIZE + ThreadIndex;
|
|
const bool bIsTileValid = LinearIndex < Substrate.ClosureTileCountBuffer[0];
|
|
FReflectionTileData TileData = (FReflectionTileData)0;
|
|
if (bIsTileValid)
|
|
{
|
|
const FSubstrateClosureTile Tile = UnpackClosureTile(Substrate.ClosureTileBuffer[LinearIndex]);
|
|
TileData.Coord = Tile.TileCoord;
|
|
TileData.ClosureIndex = Tile.ClosureIndex;
|
|
}
|
|
const bool bIsValid = bIsTileValid && all(TileData.Coord < TileViewportDimensions);
|
|
#else
|
|
// ZOrder tiles to maximize screen locality after converting to 1d for compaction
|
|
// The tile locality ultimately affects trace coherency, since trace compaction pulls from neighboring tiles
|
|
FReflectionTileData TileData;
|
|
TileData.Coord = GroupId.xy * THREADGROUP_SIZE + ThreadOffset;
|
|
TileData.ClosureIndex = SUBSTRATE_GBUFFER_FORMAT == 1 && SUBSTRATE_MATERIAL_CLOSURE_COUNT > 1 ? GroupId.z : 0;
|
|
const bool bIsValid = all(TileData.Coord < TileViewportDimensions);
|
|
#endif
|
|
|
|
if (bIsValid)
|
|
{
|
|
bool bTileUsed;
|
|
|
|
#if SUPPORT_DOWNSAMPLE_FACTOR
|
|
bTileUsed = SharedTileUsed[ThreadOffset.y * THREADGROUP_SIZE + ThreadOffset.x];
|
|
#else
|
|
const uint3 TileCoordFlatten = uint3(TileData.Coord + ResolveTileViewportMin, TileData.ClosureIndex);
|
|
bTileUsed = LumenTileBitmask[TileCoordFlatten] & LUMEN_TILE_BITMASK_REFLECTIONS;
|
|
#endif
|
|
|
|
// Active tiles
|
|
{
|
|
uint NumTilesInWave = WaveActiveCountBits(bTileUsed);
|
|
uint GlobalTileOffset = 0;
|
|
if (WaveIsFirstLane() && NumTilesInWave > 0)
|
|
{
|
|
InterlockedAdd(RWReflectionTileIndirectArgs[0], NumTilesInWave, GlobalTileOffset);
|
|
}
|
|
|
|
GlobalTileOffset = WaveReadLaneFirst(GlobalTileOffset);
|
|
|
|
if (bTileUsed)
|
|
{
|
|
// Note: Must match encoding in WaterTileCatergorisationBuildListsCS
|
|
RWReflectionTileData[GlobalTileOffset + WavePrefixCountBits(true)] = PackTileData(TileData);
|
|
}
|
|
}
|
|
|
|
// Unused tiles
|
|
{
|
|
uint NumTilesInWave = WaveActiveCountBits(!bTileUsed);
|
|
uint GlobalTileOffset = 0;
|
|
if (WaveIsFirstLane() && NumTilesInWave > 0)
|
|
{
|
|
InterlockedAdd(RWReflectionClearTileIndirectArgs[0], NumTilesInWave, GlobalTileOffset);
|
|
}
|
|
|
|
GlobalTileOffset = WaveReadLaneFirst(GlobalTileOffset);
|
|
|
|
if (!bTileUsed)
|
|
{
|
|
// Note: Must match encoding in WaterTileCatergorisationBuildListsCS
|
|
RWReflectionClearTileData[GlobalTileOffset + WavePrefixCountBits(true)] = PackTileData(TileData);
|
|
}
|
|
}
|
|
}
|
|
|
|
#else // !WAVE_OPS
|
|
|
|
//@todo - parallel version
|
|
if (ThreadIndex == 0)
|
|
{
|
|
SharedNumTiles = 0;
|
|
SharedNumClearTiles = 0;
|
|
|
|
for (uint x = 0; x < THREADGROUP_SIZE * THREADGROUP_SIZE; x++)
|
|
{
|
|
const uint2 ThreadOffset = ZOrder2D(x, log2(THREADGROUP_SIZE));
|
|
|
|
#if PERMUTATION_OVERFLOW_TILE && SUBSTRATE_GBUFFER_FORMAT==1
|
|
const uint LinearIndex = GroupId.x * THREADGROUP_SIZE * THREADGROUP_SIZE + x;
|
|
const bool bIsTileValid = LinearIndex < Substrate.ClosureTileCountBuffer[0];
|
|
FReflectionTileData TileData = (FReflectionTileData)0;
|
|
if (bIsTileValid)
|
|
{
|
|
const FSubstrateClosureTile Tile = UnpackClosureTile(Substrate.ClosureTileBuffer[LinearIndex]);
|
|
TileData.Coord = Tile.TileCoord;
|
|
TileData.ClosureIndex = Tile.ClosureIndex;
|
|
}
|
|
const bool bIsValid = bIsTileValid && all(TileData.Coord < TileViewportDimensions);
|
|
#else
|
|
// ZOrder tiles to maximize screen locality after converting to 1d for compaction
|
|
// The tile locality ultimately affects trace coherency, since trace compaction pulls from neighboring tiles
|
|
FReflectionTileData TileData;
|
|
TileData.Coord = GroupId.xy * THREADGROUP_SIZE + ThreadOffset;
|
|
TileData.ClosureIndex = SUBSTRATE_GBUFFER_FORMAT == 1 && SUBSTRATE_MATERIAL_CLOSURE_COUNT > 1 ? GroupId.z : 0;
|
|
const bool bIsValid = all(TileData.Coord < TileViewportDimensions);
|
|
#endif
|
|
|
|
if (bIsValid)
|
|
{
|
|
bool bTileUsed;
|
|
|
|
#if SUPPORT_DOWNSAMPLE_FACTOR
|
|
bTileUsed = SharedTileUsed[ThreadOffset.y * THREADGROUP_SIZE + ThreadOffset.x];
|
|
#else
|
|
const uint3 TileCoordFlatten = uint3(TileData.Coord + ResolveTileViewportMin, TileData.ClosureIndex);
|
|
bTileUsed = LumenTileBitmask[TileCoordFlatten] & LUMEN_TILE_BITMASK_REFLECTIONS;
|
|
#endif
|
|
|
|
if (bTileUsed)
|
|
{
|
|
uint TileOffset = SharedNumTiles;
|
|
// Note: Must match encoding in WaterTileCatergorisationBuildListsCS
|
|
SharedTileData[TileOffset] = PackTileData(TileData);
|
|
SharedNumTiles = TileOffset + 1;
|
|
}
|
|
else
|
|
{
|
|
// Pack clear tiles from the other end
|
|
uint TileOffset = SharedNumClearTiles;
|
|
SharedTileData[THREADGROUP_SIZE * THREADGROUP_SIZE - 1 - TileOffset] = PackTileData(TileData);
|
|
SharedNumClearTiles = TileOffset + 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
GroupMemoryBarrierWithGroupSync();
|
|
|
|
if (ThreadIndex == 0 && SharedNumTiles > 0)
|
|
{
|
|
InterlockedAdd(RWReflectionTileIndirectArgs[0], SharedNumTiles, SharedGlobalTileOffset);
|
|
}
|
|
|
|
if (ThreadIndex == 0 && SharedNumClearTiles > 0)
|
|
{
|
|
InterlockedAdd(RWReflectionClearTileIndirectArgs[0], SharedNumClearTiles, SharedGlobalClearTileOffset);
|
|
}
|
|
|
|
GroupMemoryBarrierWithGroupSync();
|
|
|
|
if (ThreadIndex < SharedNumTiles)
|
|
{
|
|
RWReflectionTileData[SharedGlobalTileOffset + ThreadIndex] = SharedTileData[ThreadIndex];
|
|
}
|
|
else
|
|
{
|
|
uint LocalThreadIndex = ThreadIndex - SharedNumTiles;
|
|
if (LocalThreadIndex < SharedNumClearTiles)
|
|
{
|
|
RWReflectionClearTileData[SharedGlobalClearTileOffset + LocalThreadIndex] = SharedTileData[THREADGROUP_SIZE * THREADGROUP_SIZE - 1 - LocalThreadIndex];
|
|
}
|
|
}
|
|
#endif // !WAVE_OPS
|
|
}
|
|
|
|
float GGXSamplingBias;
|
|
float MaxTraceDistance;
|
|
float RadianceCacheMinRoughness;
|
|
float RadianceCacheMaxRoughness;
|
|
float RadianceCacheMinTraceDistance;
|
|
float RadianceCacheMaxTraceDistance;
|
|
float RadianceCacheRoughnessFadeLength;
|
|
|
|
RWTexture2DArray<uint> RWRayTraceDistance;
|
|
RWTexture2DArray<float4> RWRayBuffer;
|
|
|
|
float2 GenerateRandom(uint2 InReflectionTracingCoord)
|
|
{
|
|
#define BLUE_NOISE_LUT 1
|
|
#if BLUE_NOISE_LUT
|
|
float2 E = BlueNoiseVec2(InReflectionTracingCoord, ReflectionsRayDirectionFrameIndex);
|
|
#else
|
|
uint2 RandomSeed = Rand3DPCG16(int3(InReflectionTracingCoord, ReflectionsStateFrameIndexMod8)).xy;
|
|
float2 E = Rand16ToFloat(RandomSeed);
|
|
#endif
|
|
E.y *= 1 - GGXSamplingBias;
|
|
return E;
|
|
}
|
|
|
|
float LinearStep(float Edge0, float Edge1, float X)
|
|
{
|
|
return saturate((X - Edge0) / (Edge1 - Edge0));
|
|
}
|
|
|
|
[numthreads(REFLECTION_THREADGROUP_SIZE_1D, 1, 1)]
|
|
void ReflectionGenerateRaysCS(
|
|
uint GroupId : SV_GroupID,
|
|
uint GroupThreadId : SV_GroupThreadID)
|
|
{
|
|
FReflectionTileData TileData;
|
|
uint3 ReflectionTracingCoord = GetReflectionTracingScreenCoord(GroupId, GroupThreadId, TileData);
|
|
bool bIsValid = all(ReflectionTracingCoord.xy < ReflectionTracingViewMin + ReflectionTracingViewSize);
|
|
|
|
// SvPositionForMaterialCoord is the SvPosition for primary sample, but is PixelCoord for overflow sample
|
|
float2 ScreenJitter = GetScreenTileJitter(ReflectionTracingCoord.xy);
|
|
uint2 SvPositionForMaterialCoord = min(ReflectionTracingCoord.xy * ReflectionDownsampleFactorXY + uint2(ScreenJitter + .5f), View.ViewRectMinAndSize.xy + View.ViewRectMinAndSize.zw - 1);
|
|
|
|
if (bIsValid)
|
|
{
|
|
const FLumenMaterialCoord Coord = GetLumenMaterialCoord_Reflection(SvPositionForMaterialCoord, TileData);
|
|
const FLumenMaterialData Material = ApplySmoothBias(ReadMaterialData(Coord, MaxRoughnessToTrace), true /*bTopLayerRoughness*/);
|
|
|
|
float DownsampledDepth = Material.SceneDepth;
|
|
|
|
if (NeedRayTracedReflections(Material.TopLayerRoughness, Material))
|
|
{
|
|
float2 ScreenUV = (Coord.SvPosition + .5f) * View.BufferSizeAndInvSize.zw;
|
|
float3 TranslatedWorldPosition = GetTranslatedWorldPositionFromScreenUV(ScreenUV, Material.SceneDepth);
|
|
float3 CameraVector = GetCameraVectorFromTranslatedWorldPosition(TranslatedWorldPosition);
|
|
|
|
float3 RayDirection;
|
|
float ConeAngle = 0.0f;
|
|
bool bMirrorReflectionDebug = false;
|
|
float3 V = -CameraVector;
|
|
|
|
// Use Substrate sampling routine only for opaque surface.
|
|
// When FontLayerTranslucency is enable, LumenMaterial is built from custom packed data (i.e., non-Substrate)
|
|
#if SUBSTRATE_GBUFFER_FORMAT==1 && !FRONT_LAYER_TRANSLUCENCY
|
|
if (!Substrate.bStochasticLighting)
|
|
{
|
|
FSubstrateAddressing SubstrateAddressing = GetSubstratePixelDataByteOffset(Coord.SvPosition, uint2(View.BufferSizeAndInvSize.xy), Substrate.MaxBytesPerPixel);
|
|
const FSubstratePixelHeader SubstratePixelHeader = UnpackSubstrateHeaderIn(Substrate.MaterialTextureArray, SubstrateAddressing, Substrate.TopLayerTexture);
|
|
const FSubstrateIntegrationSettings Settings = InitSubstrateIntegrationSettings(false /*bForceFullyRough*/, Substrate.bRoughDiffuse, Substrate.PeelLayersAboveDepth, Substrate.bRoughnessTracking);
|
|
|
|
#if SUBSTRATE_MATERIAL_CLOSURE_COUNT > 1
|
|
if (Coord.ClosureIndex > 0)
|
|
{
|
|
const uint OffsetAddress = UnpackClosureOffsetAtIndex(Substrate.ClosureOffsetTexture[Coord.SvPosition], Coord.ClosureIndex, SubstratePixelHeader.ClosureCount);
|
|
SubstrateSeekClosure(SubstrateAddressing, OffsetAddress);
|
|
}
|
|
#endif
|
|
FSubstrateBSDF BSDF = UnpackSubstrateBSDF(Substrate.MaterialTextureArray, SubstrateAddressing, SubstratePixelHeader);
|
|
|
|
// We set slabs BSDFs as having a single specular lob without haziness.
|
|
// This is to ensure the pdf is computed from a single lobe in order to be able to compute a matching cone angle.
|
|
BSDF.SubstrateSetBSDFRoughness(Material.TopLayerRoughness);
|
|
if (SubstrateGetBSDFType(BSDF) == SUBSTRATE_BSDF_TYPE_SLAB)
|
|
{
|
|
BSDF_SETHASHAZINESS(BSDF, 0);
|
|
}
|
|
|
|
const FSubstrateBSDFContext Context = SubstrateCreateBSDFContext(SubstratePixelHeader, BSDF, SubstrateAddressing, V);
|
|
const float2 E = GenerateRandom(ReflectionTracingCoord.xy);
|
|
const FBxDFSample Sample = SubstrateImportanceSampleBSDF(Context, E, SHADING_TERM_SPECULAR, Settings);
|
|
RayDirection = normalize(Sample.L);
|
|
ConeAngle = 1.0f / max(Sample.PDF, 0.0001f);
|
|
}
|
|
else
|
|
#endif
|
|
if (Material.TopLayerRoughness < 0.001f || bMirrorReflectionDebug)
|
|
{
|
|
RayDirection = reflect(CameraVector, Material.WorldNormal);
|
|
}
|
|
else
|
|
{
|
|
const float2 E = GenerateRandom(ReflectionTracingCoord.xy);
|
|
|
|
float3x3 TangentBasis = GetTangentBasis(Material);
|
|
float3 TangentV = mul(TangentBasis, V);
|
|
|
|
float2 Alpha = Pow2(Material.TopLayerRoughness).xx;
|
|
|
|
if (HasAnisotropy(Material))
|
|
{
|
|
GetAnisotropicRoughness(Alpha.x, Material.Anisotropy, Alpha.x, Alpha.y);
|
|
}
|
|
|
|
float4 GGXSample = ImportanceSampleVisibleGGX(E, Alpha, TangentV);
|
|
float3 WorldH = mul(GGXSample.xyz, TangentBasis);
|
|
RayDirection = reflect(CameraVector, WorldH);
|
|
ConeAngle = 1.0f / max(GGXSample.w, 0.0001f);
|
|
}
|
|
|
|
ConeAngle = max(ConeAngle, MinReflectionConeAngle);
|
|
|
|
// Encode first person-ness in the sign bit of ConeAngle/RayBuffer.w
|
|
float PackedConeAngle = Material.bIsFirstPerson ? -ConeAngle : ConeAngle;
|
|
RWRayBuffer[ReflectionTracingCoord] = float4(RayDirection, PackedConeAngle);
|
|
|
|
float TraceDistance = MaxTraceDistance;
|
|
bool bUseRadianceCache = false;
|
|
#if RADIANCE_CACHE
|
|
{
|
|
const float RadianceCacheRoughness = Material.TopLayerRoughness + BlueNoiseScalar(ReflectionTracingCoord.xy, ReflectionsStateFrameIndex) * RadianceCacheRoughnessFadeLength;
|
|
|
|
if (RadianceCacheRoughness > RadianceCacheMinRoughness)
|
|
{
|
|
float3 WorldPosition = TranslatedWorldPosition - DFHackToFloat(PrimaryView.PreViewTranslation); // LUMEN_LWC_TODO
|
|
FRadianceCacheCoverage Coverage = GetRadianceCacheCoverageWithUncertainCoverage(WorldPosition, RayDirection, InterleavedGradientNoise(ReflectionTracingCoord.xy, ReflectionsStateFrameIndexMod8));
|
|
bUseRadianceCache = Coverage.bValid;
|
|
if (Coverage.bValid)
|
|
{
|
|
float RadianceCacheAlpha = LinearStep(RadianceCacheMinRoughness, RadianceCacheMaxRoughness, RadianceCacheRoughness);
|
|
TraceDistance = lerp(RadianceCacheMaxTraceDistance, RadianceCacheMinTraceDistance, RadianceCacheAlpha);
|
|
TraceDistance = clamp(TraceDistance, Coverage.MinTraceDistanceBeforeInterpolation, MaxTraceDistance);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
RWRayTraceDistance[ReflectionTracingCoord] = PackRayTraceDistance(TraceDistance, bUseRadianceCache);
|
|
}
|
|
else
|
|
{
|
|
// Store invalid ray in sign bit
|
|
DownsampledDepth *= -1.0f;
|
|
}
|
|
|
|
RWDownsampledDepth[ReflectionTracingCoord] = DownsampledDepth;
|
|
#if SUBSTRATE_GBUFFER_FORMAT==1 && SUBSTRATE_STOCHASTIC_LIGHTING_ALLOWED && !FRONT_LAYER_TRANSLUCENCY
|
|
RWDownsampledClosureIndex[ReflectionTracingCoord.xy] = SubstratePackClosureIndex(Material.ClosureIndex);
|
|
#endif
|
|
}
|
|
#if SUBSTRATE_GBUFFER_FORMAT==1 && SUBSTRATE_MATERIAL_CLOSURE_COUNT > 1
|
|
// Check at the tile level if there are several BSDFs
|
|
else if (TileData.ClosureIndex > 0)
|
|
{
|
|
RWDownsampledDepth[ReflectionTracingCoord] = 0;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
Buffer<uint> ReflectionClearUnusedTracingTileIndirectArgs;
|
|
Buffer<uint> ReflectionClearUnusedTracingTileData;
|
|
|
|
/*
|
|
* Clear tracing tile data for tiles which will be skipped due to tile classification, but still may be read during reflection resolve
|
|
*/
|
|
[numthreads(REFLECTION_THREADGROUP_SIZE_2D, REFLECTION_THREADGROUP_SIZE_2D, 1)]
|
|
void ReflectionClearUnusedTraceTileDataCS(
|
|
uint3 GroupId : SV_GroupID,
|
|
uint3 GroupThreadId : SV_GroupThreadID)
|
|
{
|
|
uint ReflectionTileIndex = GroupId.x;
|
|
|
|
if (ReflectionTileIndex < ReflectionClearUnusedTracingTileIndirectArgs[0])
|
|
{
|
|
FReflectionTileData TileData = UnpackTileData(ReflectionClearUnusedTracingTileData[ReflectionTileIndex]);
|
|
|
|
uint3 ReflectionTracingCoord;
|
|
ReflectionTracingCoord.xy = TileData.Coord * REFLECTION_THREADGROUP_SIZE_2D + GroupThreadId.xy + ReflectionTracingViewMin;
|
|
ReflectionTracingCoord.z = TileData.ClosureIndex;
|
|
|
|
if (all(ReflectionTracingCoord.xy < ReflectionTracingViewMin + ReflectionTracingViewSize))
|
|
{
|
|
RWDownsampledDepth[ReflectionTracingCoord] = -1.0f;
|
|
|
|
#if SUBSTRATE_GBUFFER_FORMAT==1 && SUBSTRATE_STOCHASTIC_LIGHTING_ALLOWED && !FRONT_LAYER_TRANSLUCENCY
|
|
{
|
|
RWDownsampledClosureIndex[ReflectionTracingCoord] = SubstratePackClosureIndex(0);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef ReflectionClearNeighborTileCS
|
|
|
|
RWTexture2DArray<float4> RWSpecularAndSecondMoment;
|
|
RWTexture2DArray<float3> RWSpecularIndirect;
|
|
uint KernelRadiusInTiles;
|
|
|
|
groupshared uint SharedbIsTileUsed;
|
|
|
|
#include "LumenReflectionDenoiserCommon.ush"
|
|
|
|
[numthreads(THREADGROUP_SIZE, THREADGROUP_SIZE, 1)]
|
|
void ReflectionClearNeighborTileCS(uint3 GroupId : SV_GroupID, uint2 GroupThreadId : SV_GroupThreadID, uint GroupThread1D : SV_GroupIndex)
|
|
{
|
|
const uint2 TileCoord = GroupId.xy;
|
|
const uint2 PixelCoord = GroupId.xy * THREADGROUP_SIZE + GroupThreadId;
|
|
const uint LayerIndex = GroupId.z + 1; // Layer0 is correctly cleared by previous passes. This pass only process layers [1..N]
|
|
|
|
// If the tile is used, no need to clear it.
|
|
const bool bCurrentTileUsed = LumenTileBitmask[uint3(TileCoord, LayerIndex)] & LUMEN_TILE_BITMASK_REFLECTIONS;
|
|
if (bCurrentTileUsed)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (all(GroupThreadId == 0))
|
|
{
|
|
SharedbIsTileUsed = 0;
|
|
}
|
|
GroupMemoryBarrierWithGroupSync();
|
|
|
|
// If the current tile is used by any tile in a neighborhood of 'KernelRadiusInTiles' radius, mark the tile to be cleared
|
|
const uint NeighborTileCount1D = KernelRadiusInTiles*2 + 1;
|
|
if (all(GroupThreadId < NeighborTileCount1D))
|
|
{
|
|
const int2 Offset = int2(GroupThreadId) - KernelRadiusInTiles;
|
|
const uint2 Coord = int2(TileCoord) + Offset;
|
|
if (all(Coord < ResolveTileViewportDimensions))
|
|
{
|
|
const uint bUsed = LumenTileBitmask[uint3(Coord, LayerIndex)] & LUMEN_TILE_BITMASK_REFLECTIONS ? 1u : 0u;
|
|
InterlockedMax(SharedbIsTileUsed, bUsed);
|
|
}
|
|
}
|
|
GroupMemoryBarrierWithGroupSync();
|
|
|
|
// Clear all the tile's output pixels
|
|
if (SharedbIsTileUsed)
|
|
{
|
|
RWSpecularAndSecondMoment[uint3(PixelCoord, LayerIndex)] = float4(INVALID_LIGHTING, 0);
|
|
RWSpecularIndirect[uint3(PixelCoord, LayerIndex)] = INVALID_LIGHTING;
|
|
}
|
|
}
|
|
|
|
#endif // ReflectionClearNeighborTileCS |