#pragma only_renderers d3d11 playstation xboxone xboxseries vulkan metal glcore ps5 #pragma kernel Allocate #define PATCH_UTIL_USE_RW_IRRADIANCE_BUFFER #define PATCH_UTIL_USE_RW_CELL_INDEX_BUFFER #define PATCH_UTIL_USE_RW_CELL_ALLOCATION_MARK_BUFFER #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" #include "Packages/com.unity.render-pipelines.core/Runtime/Sampling/Hashes.hlsl" #include "SurfaceCacheCore/VectorLogic.hlsl" #include "SurfaceCacheCore/IrradianceCompression.hlsl" #include "SurfaceCacheCore/PatchUtil.hlsl" RWStructuredBuffer _RingConfigBuffer; RWStructuredBuffer _PatchGeometries; RWStructuredBuffer _PatchCellIndices; RWStructuredBuffer _PatchCounterSets; RWStructuredBuffer _PatchIrradiances0; RWStructuredBuffer _PatchIrradiances1; RWStructuredBuffer _CellAllocationMarks; RWStructuredBuffer _CellPatchIndices; StructuredBuffer _CascadeOffsets; Texture2D _CurrentFullResScreenDepths; Texture2D _CurrentFullResScreenFlatNormals; Texture2D _CurrentFullResScreenMotionVectors; Texture2D _PreviousLowResScreenIrradiancesL0; Texture2D _PreviousLowResScreenIrradiancesL10; Texture2D _PreviousLowResScreenIrradiancesL11; Texture2D _PreviousLowResScreenIrradiancesL12; Texture2D _PreviousLowResScreenNdcDepths; uint _FrameIdx; uint _GridSize; float _VoxelMinSize; uint _CascadeCount; uint _RingConfigOffset; float4x4 _CurrentClipToWorldTransform; float4x4 _PreviousClipToWorldTransform; float3 _GridTargetPos; uint2 _LowResScreenSize; uint2 _FullResPixelOffset; int _UseMotionVectorSeeding; uint CountLeadingZeroes(uint x) { // Note that this assumes firstbithigh(0) = -1. return 31 - firstbithigh(x); } // https://andrew-helmer.github.io/permute/ // https://graphics.pixar.com/library/MultiJitteredSampling/ uint RandomPermute(uint idx, uint len, uint seed) { const uint mask = 0xFFFFFFFF >> CountLeadingZeroes(len - 1); // This max guard is required and it is not clear why. // https://jira.unity3d.com/browse/GFXLIGHT-1616 const uint iterMax = 16; uint iterCount = 0; do { idx ^= seed; idx *= 0xe170893d; idx ^= seed >> 16; idx ^= (idx & mask) >> 4; idx ^= seed >> 8; idx *= 0x0929eb3f; idx ^= seed >> 23; idx ^= (idx & mask) >> 1; idx *= 1 | seed >> 27; idx *= 0x6935fa69; idx ^= (idx & mask) >> 11; idx *= 0x74dcb303; idx ^= (idx & mask) >> 2; idx *= 0x9e501cc3; idx ^= (idx & mask) >> 2; idx *= 0xc860a3df; idx &= mask; idx ^= idx >> 5; iterCount++; } while (idx >= len && iterCount < iterMax); return idx; } #define GROUP_SIZE 8 [numthreads(GROUP_SIZE, GROUP_SIZE, 1)] void Allocate(uint2 lowResPixelPos : SV_DispatchThreadID) { if (any(lowResPixelPos >= _LowResScreenSize)) return; const uint lowResThreadPosIndex = lowResPixelPos.y * _LowResScreenSize.x + lowResPixelPos.x; const uint shuffleHash = LowBiasHash32(_FrameIdx, 1); // We pass a seed of 1 to avoid the degenerate zero case. const uint shuffledLowResThreadPosIndex = RandomPermute(lowResThreadPosIndex, _LowResScreenSize.x * _LowResScreenSize.y, shuffleHash); lowResPixelPos = uint2(shuffledLowResThreadPosIndex % _LowResScreenSize.x, shuffledLowResThreadPosIndex / _LowResScreenSize.x); const uint2 fullResPixelPos = lowResPixelPos * lowResScreenScaling + _FullResPixelOffset; uint2 fullResScreenSize; _CurrentFullResScreenDepths.GetDimensions(fullResScreenSize.x, fullResScreenSize.y); if (any(fullResPixelPos >= fullResScreenSize)) return; const float ndcDepth = LoadNdcDepth(_CurrentFullResScreenDepths, fullResPixelPos); if (ndcDepth == invalidNdcDepth) return; const float2 rcpFullResScreenSize = rcp(float2(fullResScreenSize)); const float2 sourceUvPos = PixelPosToUvPos(fullResPixelPos, rcpFullResScreenSize); const float3 sourceWorldPosition = ComputeWorldSpacePosition(sourceUvPos, ndcDepth, _CurrentClipToWorldTransform); const float3 sourceWorldFlatNormal = _CurrentFullResScreenFlatNormals[fullResPixelPos]; PatchUtil::VolumePositionResolution patchPosResolution = PatchUtil::ResolveVolumePosition(sourceWorldPosition, _GridTargetPos, _GridSize, _CascadeOffsets, _CascadeCount, _VoxelMinSize); if (patchPosResolution.isValid()) { const uint directionIdx = PatchUtil::GetDirectionIndex(sourceWorldFlatNormal, PatchUtil::gridCellAngularResolution); const uint3 positionStorageSpace = PatchUtil::ConvertGridSpaceToStorageSpace(patchPosResolution.positionGridSpace, _GridSize, _CascadeOffsets[patchPosResolution.cascadeIdx]); const uint cellIdx = PatchUtil::GetCellIndex(patchPosResolution.cascadeIdx, positionStorageSpace, directionIdx, _GridSize, PatchUtil::gridCellAngularResolution); PatchUtil::PatchIndexResolutionResult resolutionResult = PatchUtil::ResolvePatchIndex( _RingConfigBuffer, _RingConfigOffset, _CellPatchIndices, _CellAllocationMarks, cellIdx); if (resolutionResult.code != PatchUtil::patchIndexResolutionCodeAllocationFailure) { PatchUtil::PatchGeometry geo; geo.position = sourceWorldPosition; geo.normal = sourceWorldFlatNormal; _PatchGeometries[resolutionResult.patchIdx] = geo; } if (resolutionResult.code == PatchUtil::patchIndexResolutionCodeAllocationSuccess) { _PatchCellIndices[resolutionResult.patchIdx] = cellIdx; PatchUtil::PatchCounterSet counterSet; PatchUtil::Reset(counterSet); PatchUtil::SetLastAccessFrame(counterSet, _FrameIdx); SphericalHarmonics::RGBL1 irradianceSeed = (SphericalHarmonics::RGBL1)0; if (_FrameIdx != 0) { if (_UseMotionVectorSeeding) { const float2 threadUvPos = PixelPosToUvPos(fullResPixelPos, rcpFullResScreenSize); float2 reprojectedThreadUvPos = threadUvPos - _CurrentFullResScreenMotionVectors[fullResPixelPos]; if (all(VECTOR_LOGIC_AND(0.0f < reprojectedThreadUvPos, reprojectedThreadUvPos < 1.0f))) { const uint2 reprojectedThreadPixelPos = reprojectedThreadUvPos * fullResScreenSize; const uint2 seedLowResPixelPos = reprojectedThreadPixelPos / lowResScreenScaling; const float seedNdcDepth = _PreviousLowResScreenNdcDepths[seedLowResPixelPos]; if (seedNdcDepth != invalidNdcDepth) { const float2 seedFullResUvPos = PixelPosToUvPos(seedLowResPixelPos * lowResScreenScaling, rcpFullResScreenSize); const float3 seedWorldPosition = ComputeWorldSpacePosition(seedFullResUvPos, seedNdcDepth, _PreviousClipToWorldTransform); const float voxelSize = PatchUtil::GetVoxelSize(_VoxelMinSize, patchPosResolution.cascadeIdx); const float patchPlaneToSeedPositionDistance = dot(sourceWorldFlatNormal, seedWorldPosition - sourceWorldPosition); if (patchPlaneToSeedPositionDistance < voxelSize) { irradianceSeed = IrradianceCompression::LoadAndDecompress( _PreviousLowResScreenIrradiancesL0, _PreviousLowResScreenIrradiancesL10, _PreviousLowResScreenIrradiancesL11, _PreviousLowResScreenIrradiancesL12, seedLowResPixelPos); if (PatchUtil::IsValid(irradianceSeed)) PatchUtil::SetUpdateCount(counterSet, PatchUtil::updateMax / 2); #ifdef SEED_DEBUGGING else { irradianceSeed = (SphericalHarmonics::RGBL1)0; PatchUtil::SetUpdateCount(counterSet, PatchUtil::updateMax); irradianceSeed.l0 = float3(10, 0, 0); } #endif } #ifdef SEED_DEBUGGING else { PatchUtil::SetUpdateCount(counterSet, PatchUtil::updateMax); irradianceSeed.l0 = float3(0, 0, 10); } #endif } } } else { irradianceSeed = IrradianceCompression::LoadAndDecompress( _PreviousLowResScreenIrradiancesL0, _PreviousLowResScreenIrradiancesL10, _PreviousLowResScreenIrradiancesL11, _PreviousLowResScreenIrradiancesL12, lowResPixelPos); if (PatchUtil::IsValid(irradianceSeed)) PatchUtil::SetUpdateCount(counterSet, PatchUtil::updateMax / 2); } #ifdef SEED_DEBUGGING // For debugging: Add color to unseeded patches. if (PatchUtil::GetUpdateCount(counterSet) == 0) { PatchUtil::SetUpdateCount(counterSet, PatchUtil::updateMax); irradianceSeed.l0 = float3(0, 10, 0); } #endif } _PatchIrradiances0[resolutionResult.patchIdx] = irradianceSeed; _PatchIrradiances1[resolutionResult.patchIdx] = irradianceSeed; _PatchCounterSets[resolutionResult.patchIdx] = counterSet; } } }