// Copyright Epic Games, Inc. All Rights Reserved. /*============================================================================= BrickAllocationManagement.usf =============================================================================*/ #include "/Engine/Public/Platform.ush" #include "BrickAllocationDefs.ush" #include "/Engine/Generated/UniformBuffers/VLMVoxelizationParams.ush" #include "/Engine/Private/MortonCode.ush" int3 VolumeSize; RWTexture3D VoxelizeVolume; int BrickSize; int MipLevel; int bIsHighestMip; [numthreads(4, 4, 4)] void ClearVolumeCS(uint3 VoxelPos : SV_DispatchThreadID) { if (any(VoxelPos >= VolumeSize)) return; VoxelizeVolume[VoxelPos] = BRICK_NOT_ALLOCATED; } float3 ImportanceVolumeMin; float3 ImportanceVolumeMax; [numthreads(4, 4, 4)] void VoxelizeImportanceVolumeCS(uint3 VoxelPos : SV_DispatchThreadID) { if (any(VoxelPos >= VolumeSize)) return; float3 WorldPosition = ((float3)VoxelPos / VLMVoxelizationParams.VolumeMaxDim - 0.5f) * 2.0f * VLMVoxelizationParams.VolumeExtent.xyz + VLMVoxelizationParams.VolumeCenter.xyz; if (all(WorldPosition >= ImportanceVolumeMin) && all(WorldPosition <= ImportanceVolumeMax)) { VoxelizeVolume[VoxelPos] = BRICK_IN_IMPORTANCE_VOLUME; } } RWTexture3D VoxelizeVolumePrevMip; [numthreads(4, 4, 4)] void DownsampleVolumeCS( uint3 VoxelPosPrevMip : SV_DispatchThreadID, uint3 VoxelPos : SV_GroupID ) { // We have 2 waves in each group, any of them evaluating to true will resulting in true if (WaveActiveAnyTrue((VoxelizeVolumePrevMip[VoxelPosPrevMip] == BRICK_ALLOCATED || VoxelizeVolumePrevMip[VoxelPosPrevMip] == BRICK_DILATED) || bIsHighestMip == 1)) { VoxelizeVolume[VoxelPos] = BRICK_ALLOCATED; } } RWTexture3D IndirectionTexture; RWBuffer BrickRequests; RWBuffer BrickRequestsUnsorted; RWBuffer BrickRequestKeys; RWBuffer BrickRequestSortedIndices; RWBuffer BrickRequestSortedIndicesInverse; [numthreads(4, 4, 4)] void CountNumBricksCS( uint3 VoxelPos : SV_DispatchThreadID ) { if (any(VoxelPos >= VolumeSize)) return; if (VoxelizeVolume[VoxelPos] == BRICK_IN_IMPORTANCE_VOLUME) { VoxelizeVolume[VoxelPos] = BRICK_NOT_ALLOCATED; } if (VoxelizeVolume[VoxelPos] == BRICK_ALLOCATED || VoxelizeVolume[VoxelPos] == BRICK_DILATED) { int BrickLinearAddress; InterlockedAdd(NumBricksRequested, 1, BrickLinearAddress); VoxelizeVolume[VoxelPos] = 1 + BrickLinearAddress; } } [numthreads(4, 4, 4)] void GatherBrickRequestsCS( uint3 VoxelPos : SV_DispatchThreadID ) { if (any(VoxelPos >= VolumeSize)) return; if (VoxelizeVolume[VoxelPos]) { int LinearIndex = VoxelizeVolume[VoxelPos] - 1; BrickRequestKeys[LinearIndex] = ((2 - MipLevel) << 30) | MortonCode3(VoxelPos.x) | (MortonCode3(VoxelPos.y) << 1) | (MortonCode3(VoxelPos.z) << 2); BrickRequestSortedIndices[LinearIndex] = LinearIndex; BrickRequestsUnsorted[LinearIndex] = uint4(VoxelPos, BrickSize); } } [numthreads(4, 4, 4)] void SplatVolumeCS( uint3 VoxelPos : SV_DispatchThreadID ) { if (any(VoxelPos >= VolumeSize)) return; if (VoxelizeVolume[VoxelPos / BrickSize]) { IndirectionTexture[VoxelPos] = uint4(ComputeBrickLayoutPosition(VoxelizeVolume[VoxelPos / BrickSize] - 1, uint3(256, 256, 256)), BrickSize); } else { if (bIsHighestMip) { IndirectionTexture[VoxelPos] = uint4(0, 0, 0, 0); } } } int NumTotalBricks; [numthreads(64, 1, 1)] void WriteSortedBrickRequestsCS(uint3 RequestIndex : SV_DispatchThreadID) { if (RequestIndex.x >= NumTotalBricks) return; BrickRequests[RequestIndex.x] = BrickRequestsUnsorted[BrickRequestSortedIndices[RequestIndex.x]]; BrickRequestSortedIndicesInverse[BrickRequestSortedIndices[RequestIndex.x]] = RequestIndex.x; } [numthreads(4, 4, 4)] void PermuteVoxelizeVolumeCS( uint3 VoxelPos : SV_DispatchThreadID ) { if (any(VoxelPos >= VolumeSize)) return; if (VoxelizeVolume[VoxelPos]) { int LinearIndex = VoxelizeVolume[VoxelPos] - 1; VoxelizeVolume[VoxelPos] = BrickRequestSortedIndicesInverse[LinearIndex] + 1; } } int3 IndirectionTextureDim; int3 BrickDataDimensions; Texture3D AmbientVector; Texture3D SHCoefficients0R; Texture3D SHCoefficients1R; Texture3D SHCoefficients0G; Texture3D SHCoefficients1G; Texture3D SHCoefficients0B; Texture3D SHCoefficients1B; Texture3D SkyBentNormal; Texture3D DirectionalLightShadowing; RWTexture3D OutAmbientVector; RWTexture3D OutSHCoefficients0R; RWTexture3D OutSHCoefficients1R; RWTexture3D OutSHCoefficients0G; RWTexture3D OutSHCoefficients1G; RWTexture3D OutSHCoefficients0B; RWTexture3D OutSHCoefficients1B; RWTexture3D OutSkyBentNormal; RWTexture3D OutDirectionalLightShadowing; uint FrameNumber; float3 DilateWithValidityMask(RWTexture3D Texture, RWTexture3D CoverageMask, float3 UVW) { float AccumulatedWeight = 0.0f; float3 Value = float3(0, 0, 0); float BrickSize; const int PaddedBrickSize = 5; { float3 IndirectionTextureUVW = UVW; int3 IndirectionTextureCoordinates = floor(IndirectionTextureUVW); uint4 IndirectionTextureValue = IndirectionTexture[IndirectionTextureCoordinates]; BrickSize = IndirectionTextureValue.w; } for (int z = -1; z <= 1; z++) { for (int y = -1; y <= 1; y++) { for (int x = -1; x <= 1; x++) { float3 IndirectionTextureUVW = UVW + int3(x, y, z) / BrickSize / 4.0f; int3 IndirectionTextureCoordinates = floor(IndirectionTextureUVW); uint4 IndirectionTextureValue = IndirectionTexture[IndirectionTextureCoordinates]; int3 BrickOrigin = (IndirectionTextureCoordinates / IndirectionTextureValue.w) * IndirectionTextureValue.w; // Integer Div float3 BrickUVW = IndirectionTextureValue.xyz * PaddedBrickSize + frac(IndirectionTextureUVW / IndirectionTextureValue.w) * 4; int3 Coord = BrickUVW; if (CoverageMask[Coord] == 1) { Value += Texture[Coord]; AccumulatedWeight += 1; } } } } return Value / max(AccumulatedWeight, 0.00001f); } UNORM float4 DilateWithValidityMask(RWTexture3D Texture, RWTexture3D CoverageMask, float3 UVW) { int3 Coord = UVW; float3 CoordinateFraction = frac(UVW); float AccumulatedWeight = 0.0f; float4 Value = float4(0, 0, 0, 0); float BrickSize; const int PaddedBrickSize = 5; { float3 IndirectionTextureUVW = UVW; int3 IndirectionTextureCoordinates = floor(IndirectionTextureUVW); uint4 IndirectionTextureValue = IndirectionTexture[IndirectionTextureCoordinates]; BrickSize = IndirectionTextureValue.w; } for (int z = -1; z <= 1; z++) { for (int y = -1; y <= 1; y++) { for (int x = -1; x <= 1; x++) { float3 IndirectionTextureUVW = UVW + int3(x, y, z) / BrickSize / 4.0f; int3 IndirectionTextureCoordinates = floor(IndirectionTextureUVW); uint4 IndirectionTextureValue = IndirectionTexture[IndirectionTextureCoordinates]; int3 BrickOrigin = (IndirectionTextureCoordinates / IndirectionTextureValue.w) * IndirectionTextureValue.w; // Integer Div float3 BrickUVW = IndirectionTextureValue.xyz * PaddedBrickSize + frac(IndirectionTextureUVW / IndirectionTextureValue.w) * 4; int3 Coord = BrickUVW; if (CoverageMask[Coord] == 1) { Value += Texture[Coord]; AccumulatedWeight += 1; } } } } return Value / max(AccumulatedWeight, 0.00001f); } UNORM float DilateWithValidityMask(RWTexture3D Texture, RWTexture3D CoverageMask, float3 UVW) { int3 Coord = UVW; float3 CoordinateFraction = frac(UVW); float AccumulatedWeight = 0.0f; float Value = 0; float BrickSize; const int PaddedBrickSize = 5; { float3 IndirectionTextureUVW = UVW; int3 IndirectionTextureCoordinates = floor(IndirectionTextureUVW); uint4 IndirectionTextureValue = IndirectionTexture[IndirectionTextureCoordinates]; BrickSize = IndirectionTextureValue.w; } for (int z = -1; z <= 1; z++) { for (int y = -1; y <= 1; y++) { for (int x = -1; x <= 1; x++) { float3 IndirectionTextureUVW = UVW + int3(x, y, z) / BrickSize / 4.0f; int3 IndirectionTextureCoordinates = floor(IndirectionTextureUVW); uint4 IndirectionTextureValue = IndirectionTexture[IndirectionTextureCoordinates]; int3 BrickOrigin = (IndirectionTextureCoordinates / IndirectionTextureValue.w) * IndirectionTextureValue.w; // Integer Div float3 BrickUVW = IndirectionTextureValue.xyz * PaddedBrickSize + frac(IndirectionTextureUVW / IndirectionTextureValue.w) * 4; int3 Coord = BrickUVW; if (CoverageMask[Coord] == 1) { Value += Texture[Coord]; AccumulatedWeight += 1; } } } } return Value / max(AccumulatedWeight, 0.00001f); } float3 ManualTrilinearFilter(RWTexture3D Texture, float3 UVW) { int3 Coord = UVW; float3 CoordinateFraction = frac(UVW); float3 Weight; float3 Value = float3(0, 0, 0); for (int z = 0; z <= 1; z++) { Weight.z = (z == 0 ? 1 - CoordinateFraction.z : CoordinateFraction.z); for (int y = 0; y <= 1; y++) { Weight.y = (y == 0 ? 1 - CoordinateFraction.y : CoordinateFraction.y); for (int x = 0; x <= 1; x++) { Weight.x = (x == 0 ? 1 - CoordinateFraction.x : CoordinateFraction.x); Value += Texture[Coord + int3(x, y, z)] * Weight.x * Weight.y * Weight.z; } } } return Value; } UNORM float4 ManualTrilinearFilter(RWTexture3D Texture, float3 UVW) { int3 Coord = UVW; float3 CoordinateFraction = frac(UVW); float3 Weight; float4 Value = float4(0, 0, 0, 0); for (int z = 0; z <= 1; z++) { Weight.z = (z == 0 ? 1 - CoordinateFraction.z : CoordinateFraction.z); for (int y = 0; y <= 1; y++) { Weight.y = (y == 0 ? 1 - CoordinateFraction.y : CoordinateFraction.y); for (int x = 0; x <= 1; x++) { Weight.x = (x == 0 ? 1 - CoordinateFraction.x : CoordinateFraction.x); Value += Texture[Coord + int3(x, y, z)] * Weight.x * Weight.y * Weight.z; } } } return Value; } float ManualTrilinearFilter(RWTexture3D Texture, float3 UVW) { int3 Coord = UVW; float3 CoordinateFraction = frac(UVW); float3 Weight; float Value = 0; for (int z = 0; z <= 1; z++) { Weight.z = (z == 0 ? 1 - CoordinateFraction.z : CoordinateFraction.z); for (int y = 0; y <= 1; y++) { Weight.y = (y == 0 ? 1 - CoordinateFraction.y : CoordinateFraction.y); for (int x = 0; x <= 1; x++) { Weight.x = (x == 0 ? 1 - CoordinateFraction.x : CoordinateFraction.x); Value += Texture[Coord + int3(x, y, z)] * Weight.x * Weight.y * Weight.z; } } } return Value; } int BrickBatchOffset; RWTexture3D ValidityMask; [numthreads(5, 5, 5)] void CopyPaddingStripsCS(uint3 BrickID : SV_GroupID, uint3 CellPosInBrick : SV_GroupThreadID) { uint BrickIndex = BrickID.x + BrickBatchOffset; if (BrickIndex >= NumTotalBricks) return; int3 CellPosInCurrentMip = BrickRequests[BrickIndex].xyz * 4 + CellPosInBrick; float3 InvCellSizeInCurrentMip = float3(1, 1, 1) / 4.0f; float PaddedBrickSize = 4 + 1; uint3 VoxelPos = ComputeBrickLayoutPosition(BrickIndex, uint3(256, 256, 256)) * PaddedBrickSize + CellPosInBrick; if (BrickRequests[BrickIndex].w == 1u << (2 * MipLevel)) { int3 VoxelizeVolumeCoords = floor(CellPosInCurrentMip * InvCellSizeInCurrentMip); int BrickLinearAddress = VoxelizeVolume[VoxelizeVolumeCoords] - 1; if (BrickLinearAddress >= 0) { int3 BrickOrigin = ComputeBrickLayoutPosition(BrickLinearAddress, uint3(256, 256, 256)); int3 BrickUVW = 5 * BrickOrigin + 4 * frac(CellPosInCurrentMip * InvCellSizeInCurrentMip); OutAmbientVector[VoxelPos] = OutAmbientVector[BrickUVW]; OutSHCoefficients0R[VoxelPos] = OutSHCoefficients0R[BrickUVW]; OutSHCoefficients1R[VoxelPos] = OutSHCoefficients1R[BrickUVW]; OutSHCoefficients0G[VoxelPos] = OutSHCoefficients0G[BrickUVW]; OutSHCoefficients1G[VoxelPos] = OutSHCoefficients1G[BrickUVW]; OutSHCoefficients0B[VoxelPos] = OutSHCoefficients0B[BrickUVW]; OutSHCoefficients1B[VoxelPos] = OutSHCoefficients1B[BrickUVW]; OutSkyBentNormal[VoxelPos] = OutSkyBentNormal[BrickUVW]; OutDirectionalLightShadowing[VoxelPos] = OutDirectionalLightShadowing[BrickUVW]; ValidityMask[VoxelPos] = ValidityMask[BrickUVW]; } } } [numthreads(5, 5, 5)] void FillInvalidCellCS(uint3 BrickID : SV_GroupID, uint3 CellPosInBrick : SV_GroupThreadID) { uint BrickIndex = BrickID.x + BrickBatchOffset; if (BrickIndex >= NumTotalBricks) return; int3 CellPosInVLM = (BrickRequests[BrickIndex].xyz * 4 + CellPosInBrick) * BrickRequests[BrickIndex].w; float3 InvCellSizeInVLM = float3(1, 1, 1) / 4.0f; float PaddedBrickSize = 4 + 1; uint3 VoxelPos = ComputeBrickLayoutPosition(BrickIndex, uint3(256, 256, 256)) * PaddedBrickSize + CellPosInBrick; if (ValidityMask[VoxelPos] == 0) { float3 IndirectionTextureUVW = CellPosInVLM * InvCellSizeInVLM; OutAmbientVector[VoxelPos] = DilateWithValidityMask(OutAmbientVector, ValidityMask, IndirectionTextureUVW); OutSHCoefficients0R[VoxelPos] = DilateWithValidityMask(OutSHCoefficients0R, ValidityMask, IndirectionTextureUVW); OutSHCoefficients1R[VoxelPos] = DilateWithValidityMask(OutSHCoefficients1R, ValidityMask, IndirectionTextureUVW); OutSHCoefficients0G[VoxelPos] = DilateWithValidityMask(OutSHCoefficients0G, ValidityMask, IndirectionTextureUVW); OutSHCoefficients1G[VoxelPos] = DilateWithValidityMask(OutSHCoefficients1G, ValidityMask, IndirectionTextureUVW); OutSHCoefficients0B[VoxelPos] = DilateWithValidityMask(OutSHCoefficients0B, ValidityMask, IndirectionTextureUVW); OutSHCoefficients1B[VoxelPos] = DilateWithValidityMask(OutSHCoefficients1B, ValidityMask, IndirectionTextureUVW); OutSkyBentNormal[VoxelPos] = DilateWithValidityMask(OutSkyBentNormal, ValidityMask, IndirectionTextureUVW); OutDirectionalLightShadowing[VoxelPos] = DilateWithValidityMask(OutDirectionalLightShadowing, ValidityMask, IndirectionTextureUVW); } } [numthreads(5, 5, 5)] void StitchBorderCS(uint3 BrickID : SV_GroupID, uint3 CellPosInBrick : SV_GroupThreadID) { uint BrickIndex = BrickID.x + BrickBatchOffset; if (BrickIndex >= NumTotalBricks) return; int3 CellPosInVLM = (BrickRequests[BrickIndex].xyz * 4 + CellPosInBrick) * BrickRequests[BrickIndex].w; float3 InvCellSizeInVLM = float3(1, 1, 1) / 4.0f; float PaddedBrickSize = 4 + 1; uint3 VoxelPos = ComputeBrickLayoutPosition(BrickIndex, uint3(256, 256, 256)) * PaddedBrickSize + CellPosInBrick; for (int dx = -1; dx <= 1; dx++) { for (int dy = -1; dy <= 1; dy++) { for (int dz = -1; dz <= 1; dz++) { int3 IndirectionTextureCoordinates = floor(CellPosInVLM * InvCellSizeInVLM + InvCellSizeInVLM * 0.5f * int3(dx, dy, dz)); uint4 IndirectionTextureValue = IndirectionTexture[IndirectionTextureCoordinates]; if (IndirectionTextureValue.w > BrickRequests[BrickIndex].w) { int3 BrickOrigin = (IndirectionTextureCoordinates / IndirectionTextureValue.w) * IndirectionTextureValue.w; // Integer Div float3 BrickUVW = (IndirectionTextureValue.xyz * PaddedBrickSize + saturate((CellPosInVLM * InvCellSizeInVLM - BrickOrigin) / IndirectionTextureValue.w) * 4); OutAmbientVector[VoxelPos] = ManualTrilinearFilter(OutAmbientVector, BrickUVW); OutSHCoefficients0R[VoxelPos] = ManualTrilinearFilter(OutSHCoefficients0R, BrickUVW); OutSHCoefficients1R[VoxelPos] = ManualTrilinearFilter(OutSHCoefficients1R, BrickUVW); OutSHCoefficients0G[VoxelPos] = ManualTrilinearFilter(OutSHCoefficients0G, BrickUVW); OutSHCoefficients1G[VoxelPos] = ManualTrilinearFilter(OutSHCoefficients1G, BrickUVW); OutSHCoefficients0B[VoxelPos] = ManualTrilinearFilter(OutSHCoefficients0B, BrickUVW); OutSHCoefficients1B[VoxelPos] = ManualTrilinearFilter(OutSHCoefficients1B, BrickUVW); OutSkyBentNormal[VoxelPos] = ManualTrilinearFilter(OutSkyBentNormal, BrickUVW); OutDirectionalLightShadowing[VoxelPos] = ManualTrilinearFilter(OutDirectionalLightShadowing, BrickUVW); } } } } } int NumTotalPassesToRender; [numthreads(5, 5, 5)] void FinalizeBrickResultsCS(uint3 BrickID : SV_GroupID, uint3 CellPosInBrick : SV_GroupThreadID) { uint BrickIndex = BrickID.x + BrickBatchOffset; if (BrickIndex >= NumTotalBricks) return; int3 CellPosInVLM = (BrickRequests[BrickIndex].xyz * 4 + CellPosInBrick) * BrickRequests[BrickIndex].w; float3 InvCellSizeInVLM = float3(1, 1, 1) / 4.0f; float PaddedBrickSize = 4 + 1; uint3 VoxelPos = ComputeBrickLayoutPosition(BrickID.x, uint3(256, 256, 256)) * PaddedBrickSize + CellPosInBrick; uint3 OutVoxelPos = ComputeBrickLayoutPosition(BrickIndex, uint3(256, 256, 256)) * PaddedBrickSize + CellPosInBrick; uint SampleCount = asuint(AmbientVector[VoxelPos].w); if (SampleCount > 0) { OutAmbientVector[OutVoxelPos] = AmbientVector[VoxelPos].xyz / SampleCount; float3 InvAmbient = float3(1.0f, 1.0f, 1.0f) / max(AmbientVector[VoxelPos].xyz / SampleCount, float3(0.0001f, 0.0001f, 0.0001f)); float4 CoefficientNormalizationScale0 = float4( 0.282095f / 0.488603f, 0.282095f / 0.488603f, 0.282095f / 0.488603f, 0.282095f / 1.092548f); float4 CoefficientNormalizationScale1 = float4( 0.282095f / 1.092548f, 0.282095f / (4.0f * 0.315392f), 0.282095f / 1.092548f, 0.282095f / (2.0f * 0.546274f)); OutSHCoefficients0R[OutVoxelPos] = (SHCoefficients0R[VoxelPos] / SampleCount) * InvAmbient.r * CoefficientNormalizationScale0 * 0.5f + 0.5f; OutSHCoefficients1R[OutVoxelPos] = (SHCoefficients1R[VoxelPos] / SampleCount) * InvAmbient.r * CoefficientNormalizationScale1 * 0.5f + 0.5f; OutSHCoefficients0G[OutVoxelPos] = (SHCoefficients0G[VoxelPos] / SampleCount) * InvAmbient.g * CoefficientNormalizationScale0 * 0.5f + 0.5f; OutSHCoefficients1G[OutVoxelPos] = (SHCoefficients1G[VoxelPos] / SampleCount) * InvAmbient.g * CoefficientNormalizationScale1 * 0.5f + 0.5f; OutSHCoefficients0B[OutVoxelPos] = (SHCoefficients0B[VoxelPos] / SampleCount) * InvAmbient.b * CoefficientNormalizationScale0 * 0.5f + 0.5f; OutSHCoefficients1B[OutVoxelPos] = (SHCoefficients1B[VoxelPos] / SampleCount) * InvAmbient.b * CoefficientNormalizationScale1 * 0.5f + 0.5f; OutSkyBentNormal[OutVoxelPos] = SkyBentNormal[VoxelPos] / SampleCount * 0.5f + 0.5f; OutDirectionalLightShadowing[OutVoxelPos] = DirectionalLightShadowing[VoxelPos]; } ValidityMask[OutVoxelPos] = SampleCount > NumTotalPassesToRender / 3; }