Skip to content

Commit d056c4f

Browse files
urasmusEvergreen
authored andcommitted
Fix jittering artifact in Surface Cache caused by "rank flipping"
1 parent 8deab59 commit d056c4f

9 files changed

Lines changed: 175 additions & 116 deletions

File tree

Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/Estimation.hlsl

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ void ProcessAndStoreRadianceSample(RWStructuredBuffer<SphericalHarmonics::RGBL1>
5757
SphericalHarmonics::CosineConvolve(radianceSample);
5858

5959
PatchUtil::PatchStatisticsSet oldStats = patchStatistics[patchIdx];
60-
const uint oldUpdateCount = PatchUtil::GetUpdateCount(oldStats.patchCounters);
60+
const uint oldUpdateCount = PatchUtil::GetUpdateCount(oldStats.counters);
6161
const uint newUpdateCount = min(oldUpdateCount + 1, PatchUtil::updateMax);
6262

6363
const SphericalHarmonics::RGBL1 oldIrradiance = patchIrradiances[patchIdx];
@@ -79,9 +79,8 @@ void ProcessAndStoreRadianceSample(RWStructuredBuffer<SphericalHarmonics::RGBL1>
7979
PatchUtil::PatchStatisticsSet newStats;
8080
newStats.mean = newL0ShortIrradiance;
8181
newStats.variance = newVariance;
82-
newStats.patchCounters = oldStats.patchCounters;
83-
PatchUtil::SetUpdateCount(newStats.patchCounters, newUpdateCount);
84-
newStats.rank = oldStats.rank;
82+
newStats.counters = oldStats.counters;
83+
PatchUtil::SetUpdateCount(newStats.counters, newUpdateCount);
8584
patchStatistics[patchIdx] = newStats;
8685
}
8786

@@ -223,7 +222,7 @@ void Estimate(UnifiedRT::DispatchInfo dispatchInfo)
223222
QrngKronecker2D rng;
224223

225224
const PatchUtil::PatchGeometry patchGeo = _PatchGeometries[patchIdx];
226-
bool enablePatchAllocation = (_PatchStatistics[patchIdx].rank == 0);
225+
bool enablePatchAllocation = (PatchUtil::GetRank(_PatchStatistics[patchIdx].counters) == 0);
227226

228227
MaterialPoolParamSet matPoolParams;
229228
matPoolParams.materialEntries = _MaterialEntries;

Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/Eviction.compute

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -11,41 +11,40 @@ RWStructuredBuffer<uint> _PatchCellIndices;
1111

1212
uint _RingConfigOffset;
1313
uint _FrameIdx;
14-
15-
uint ModuloDistance(uint a, uint b, uint modulo)
16-
{
17-
int dif = abs(int(a) - int(b));
18-
return min(dif, modulo - dif);
19-
}
14+
uint _BouncePatchAllocation;
2015

2116
[numthreads(256, 1, 1)]
2217
void Evict(uint patchIdx : SV_DispatchThreadID)
2318
{
2419
if (!RingBuffer::IsPositionInUse(_RingConfigBuffer, _RingConfigOffset, patchIdx))
2520
return;
2621

27-
const uint evictionThreshold = _PatchStatistics[patchIdx].rank == 0 ? 60 * 4 : 60 * 1; // patches allocated from bounce hits are evicted sooner
28-
29-
// Reset all patches rank to 1 (that is not allowed to spawn more patches on bounces hit)
30-
// Rank will be updated to 0 by the PatchAllocation.compute shader for patches that are seen from the camera
31-
_PatchStatistics[patchIdx].rank = 1;
32-
3322
const uint cellIdx = _PatchCellIndices[patchIdx];
3423
if (cellIdx == PatchUtil::invalidCellIndex)
3524
return;
3625

37-
const PatchUtil::PatchCounterSet counterSet = _PatchStatistics[patchIdx].patchCounters;
38-
const uint lastAccessFrameIdx = PatchUtil::GetLastAccessFrame(counterSet);
26+
PatchUtil::PatchStatisticsSet stats = _PatchStatistics[patchIdx];
3927

40-
// Here we take into account that last frame access index is in [0, 2^16-1].
41-
// We use that the last frame index can never be later than current frame index.
42-
const uint modulo = 65536; // 2^16
43-
const uint frameSinceLastUse = ModuloDistance(_FrameIdx % modulo, lastAccessFrameIdx, modulo);
28+
const uint frameSinceLastAccess = PatchUtil::GetFramesSinceLastAccess(_FrameIdx, PatchUtil::GetLastAccessFrame(stats.counters));
4429

45-
if (evictionThreshold < frameSinceLastUse)
30+
if (PatchUtil::evictionThreshold < frameSinceLastAccess)
4631
{
47-
_PatchCellIndices[patchIdx] = PatchUtil::invalidCellIndex;
48-
_CellAllocationMarks[cellIdx] = 0;
49-
_CellPatchIndices[cellIdx] = PatchUtil::invalidPatchIndex;
32+
// If Bounce Patch Allocation is enabled, then rank 0 patches are
33+
// first demoted to rank 1, then later they are evicted.
34+
// If Bounce Patch Allocation is disabled we go directly to
35+
// eviction.
36+
if (_BouncePatchAllocation && PatchUtil::GetRank(stats.counters) == 0)
37+
{
38+
PatchUtil::SetRank(stats.counters, 1);
39+
PatchUtil::SetLastAccessFrame(stats.counters, _FrameIdx);
40+
_PatchStatistics[patchIdx] = stats;
41+
}
42+
else
43+
{
44+
_PatchCellIndices[patchIdx] = PatchUtil::invalidCellIndex;
45+
_CellAllocationMarks[cellIdx] = 0;
46+
_CellPatchIndices[cellIdx] = PatchUtil::invalidPatchIndex;
47+
}
5048
}
49+
5150
}

Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/PatchUtil.hlsl

Lines changed: 62 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ namespace PatchUtil
4343
static const uint volumeAngularResolution = 4; // Must match C# side.
4444
static const float3 invalidIrradiance = float3(-1, -1, -1);
4545
static const uint updateMax = 32;
46+
static const uint evictionThreshold = 60 * 4;
4647

4748
struct PatchGeometry
4849
{
@@ -52,15 +53,18 @@ namespace PatchUtil
5253

5354
struct PatchCounterSet
5455
{
56+
// Layout
57+
// 0x000000FF: Update count.
58+
// 0x0000FF00: Rank.
59+
// 0xFFFF0000: Last access frame.
5560
uint data;
5661
};
5762

5863
struct PatchStatisticsSet
5964
{
6065
float3 mean;
6166
float3 variance;
62-
PatchCounterSet patchCounters;
63-
uint rank;
67+
PatchCounterSet counters;
6468
};
6569

6670
struct VolumeParamSet
@@ -81,14 +85,41 @@ namespace PatchUtil
8185
uint ringConfigOffset;
8286
};
8387

84-
void Reset(inout PatchCounterSet set)
88+
uint ModuloDistance(uint a, uint b, uint modulo)
89+
{
90+
int dif = abs(int(a) - int(b));
91+
return min(dif, modulo - dif);
92+
}
93+
94+
uint GetFramesSinceLastAccess(uint currentFrameIdx, uint patchLastAccessFrame)
95+
{
96+
// Here we take into account that last access frame index is in [0, 2^16-1].
97+
// We use that the last frame index can never be later than current frame index.
98+
const uint modulo = 65536; // 2^16
99+
return ModuloDistance(
100+
currentFrameIdx % modulo,
101+
patchLastAccessFrame,
102+
modulo);
103+
}
104+
105+
void Reset(out PatchCounterSet set)
85106
{
86107
set.data = 0;
87108
}
88109

89110
uint GetUpdateCount(PatchCounterSet set)
90111
{
91-
return set.data & 0xFFFF;
112+
return set.data & 0xFF;
113+
}
114+
115+
uint GetRank(PatchCounterSet set)
116+
{
117+
return (set.data & 0xFF00) >> 8;
118+
}
119+
120+
void SetRank(inout PatchCounterSet set, uint rank)
121+
{
122+
set.data = (rank << 8) | (set.data & 0xFFFF00FF);
92123
}
93124

94125
uint GetLastAccessFrame(PatchCounterSet set)
@@ -98,7 +129,7 @@ namespace PatchUtil
98129

99130
void SetUpdateCount(inout PatchCounterSet set, uint updateCount)
100131
{
101-
set.data = updateCount | (set.data & 0xFFFF0000);
132+
set.data = updateCount | (set.data & 0xFFFFFF00);
102133
}
103134

104135
void SetLastAccessFrame(inout PatchCounterSet set, uint lastAccessFrame)
@@ -113,9 +144,9 @@ namespace PatchUtil
113144

114145
void WriteLastFrameAccess(RWStructuredBuffer<PatchUtil::PatchStatisticsSet> statisticsSets, uint patchIdx, uint frameIdx)
115146
{
116-
PatchCounterSet counterSet = statisticsSets[patchIdx].patchCounters;
147+
PatchCounterSet counterSet = statisticsSets[patchIdx].counters;
117148
SetLastAccessFrame(counterSet, frameIdx);
118-
statisticsSets[patchIdx].patchCounters = counterSet;
149+
statisticsSets[patchIdx].counters = counterSet;
119150
}
120151

121152
float GetVoxelSize(float voxelMinSize, uint cascadeIdx)
@@ -166,14 +197,14 @@ namespace PatchUtil
166197
{
167198
// To avoid discontinuities near the cardinal axis directions, we apply an arbitrary rotation.
168199
// This is based on the assumption that surfaces oriented along the cardinal axis directions
169-
// are most likely in a scene compared to other directions.
200+
// are more likely in a scene compared to other directions.
170201
const float3x3 arbitraryRotation = float3x3(
171202
float3(0.34034f, -0.30925f, 0.888f),
172203
float3(-0.30925f, 0.85502f, 0.41629f),
173204
float3(-0.888f, -0.41629f, 0.19536f));
174205
const float3 rotatedDirection = mul(arbitraryRotation, direction);
175206

176-
const uint2 angularSquarePos = min(uint2(3, 3), SphereToSquare(rotatedDirection) * angularResolution);
207+
const uint2 angularSquarePos = min(uint2(angularResolution - 1, angularResolution - 1), SphereToSquare(rotatedDirection) * angularResolution);
177208
return angularSquarePos.y * angularResolution + angularSquarePos.x;
178209
}
179210

@@ -321,7 +352,7 @@ namespace PatchUtil
321352
return resultBool;
322353
}
323354

324-
uint FindPatchIndex(VolumeParamSet volumeParams, StructuredBuffer<uint> cellPatchIndices, float3 worldPosition, float3 worldNormal)
355+
uint FindPatchIndex(VolumeParamSet volumeParams, CellPatchIndexBufferType cellPatchIndices, float3 worldPosition, float3 worldNormal)
325356
{
326357
VolumePositionResolution posResolution = ResolveVolumePosition(worldPosition, volumeParams);
327358
if (posResolution.isValid())
@@ -345,7 +376,7 @@ namespace PatchUtil
345376
}
346377
}
347378

348-
uint FindPatchIndexAndUpdateLastAccess(VolumeParamSet volumeParams, StructuredBuffer<uint> cellPatchIndices, RWStructuredBuffer<PatchUtil::PatchStatisticsSet> patchStatisticSets, float3 worldPosition, float3 worldNormal, uint frameIdx)
379+
uint FindPatchIndexAndUpdateLastAccess(VolumeParamSet volumeParams, CellPatchIndexBufferType cellPatchIndices, RWStructuredBuffer<PatchUtil::PatchStatisticsSet> patchStatisticSets, float3 worldPosition, float3 worldNormal, uint frameIdx)
349380
{
350381
const uint patchIdx = FindPatchIndex(volumeParams, cellPatchIndices, worldPosition, worldNormal);
351382
if (patchIdx != invalidPatchIndex)
@@ -384,12 +415,17 @@ namespace PatchUtil
384415
resultIrradiance);
385416
}
386417

418+
float3 EvalIrradiance(SphericalHarmonics::RGBL1 irradiance, float3 normal)
419+
{
420+
return max(0, SphericalHarmonics::Eval(irradiance, normal));
421+
}
422+
387423
float3 ReadPlanarIrradiance(PatchIrradianceBufferType patchIrradiances, CellPatchIndexBufferType cellPatchIndices, uint spatialResolution, uint cascadeIdx, uint3 volumeSpacePosition, float3 worldNormal)
388424
{
389425
SphericalHarmonics::RGBL1 resultIrradiance;
390426
bool resultBool = ReadHemisphericalIrradiance(patchIrradiances, cellPatchIndices, spatialResolution, cascadeIdx, volumeSpacePosition, worldNormal, resultIrradiance);
391427
if (resultBool)
392-
return max(0, SphericalHarmonics::Eval(resultIrradiance, worldNormal));
428+
return EvalIrradiance(resultIrradiance, worldNormal);
393429
else
394430
return invalidIrradiance;
395431
}
@@ -420,20 +456,16 @@ namespace PatchUtil
420456

421457
PatchStatisticsSet InitPatchStatistics(float3 irradianceSeed, uint frameIndex, uint rank)
422458
{
423-
PatchCounterSet counterSet;
424-
Reset(counterSet);
425-
PatchUtil::SetLastAccessFrame(counterSet, frameIndex);
426-
427459
PatchStatisticsSet stats;
428460
stats.mean = irradianceSeed;
429461
stats.variance = 0;
430-
stats.patchCounters = counterSet;
431-
stats.rank = rank;
462+
Reset(stats.counters);
463+
SetLastAccessFrame(stats.counters, frameIndex);
464+
SetRank(stats.counters, rank);
432465

433466
return stats;
434467
}
435468

436-
437469
#if BOUNCE_PATCH_ALLOCATION
438470
void AllocatePatch(
439471
float3 worldPosition,
@@ -461,17 +493,17 @@ namespace PatchUtil
461493
allocParams.cellAllocationMarks,
462494
cellIdx);
463495

464-
if (resolutionResult.code == PatchUtil::patchIndexResolutionCodeAllocationFailure || resolutionResult.code == PatchUtil::patchIndexResolutionCodeLookup)
465-
return;
466-
467-
PatchUtil::PatchGeometry geo;
468-
geo.position = worldPosition;
469-
geo.normal = worldNormal;
470-
patchGeometries[resolutionResult.patchIdx] = geo;
471-
472-
SphericalHarmonics::RGBL1 irradianceSeed = (SphericalHarmonics::RGBL1) 0;
473-
patchIrradiances[resolutionResult.patchIdx] = irradianceSeed;
474-
patchStatistics[resolutionResult.patchIdx] = PatchUtil::InitPatchStatistics(irradianceSeed.l0, frameIndex, /*rank*/ 1);
496+
if (resolutionResult.code == PatchUtil::patchIndexResolutionCodeAllocationSuccess)
497+
{
498+
PatchUtil::PatchGeometry geo;
499+
geo.position = worldPosition;
500+
geo.normal = worldNormal;
501+
patchGeometries[resolutionResult.patchIdx] = geo;
502+
503+
SphericalHarmonics::RGBL1 irradianceSeed = (SphericalHarmonics::RGBL1)0;
504+
patchIrradiances[resolutionResult.patchIdx] = irradianceSeed;
505+
patchStatistics[resolutionResult.patchIdx] = PatchUtil::InitPatchStatistics(irradianceSeed.l0, frameIndex, /*rank*/ 1);
506+
}
475507
}
476508
#endif
477509
}

Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/PathTracing.hlsl

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -232,9 +232,8 @@ float3 OutgoingDirectionalBounceAndMultiBounceRadiance(
232232
PatchUtil::VolumeParamSet volumeParams,
233233
float3 albedo,
234234
float3 emission,
235-
out bool multiBounceSurfaceCacheMiss)
235+
out uint bouncePatchIndex)
236236
{
237-
multiBounceSurfaceCacheMiss = false;
238237
float3 radiance = 0.0f;
239238

240239
if (any(dirLightIntensity != 0.0f))
@@ -248,21 +247,20 @@ float3 OutgoingDirectionalBounceAndMultiBounceRadiance(
248247
shadowRay.tMin = 0;
249248
shadowRay.tMax = FLT_MAX;
250249

251-
UnifiedRT::Hit hitResult2 = UnifiedRT::TraceRayClosestHit(dispatchInfo, accelStruct, 0xFFFFFFFF, shadowRay, UnifiedRT::kRayFlagNone);
252-
if (!hitResult2.IsValid())
250+
UnifiedRT::Hit hitResult = UnifiedRT::TraceRayClosestHit(dispatchInfo, accelStruct, 0xFFFFFFFF, shadowRay, UnifiedRT::kRayFlagNone);
251+
if (!hitResult.IsValid())
253252
{
254253
radiance += dirLightIntensity * dot(-dirLightDirection, normal);
255254
}
256255
}
257256
}
258257

258+
bouncePatchIndex = PatchUtil::invalidPatchIndex;
259259
if (multiBounce)
260260
{
261-
float3 cacheRead = PatchUtil::ReadPlanarIrradiance(patchIrradiances, cellPatchIndices, volumeParams, position, normal);
262-
if (all(cacheRead != PatchUtil::invalidIrradiance))
263-
radiance += cacheRead;
264-
else
265-
multiBounceSurfaceCacheMiss = true;
261+
bouncePatchIndex = PatchUtil::FindPatchIndex(volumeParams, cellPatchIndices, position, normal);
262+
if (bouncePatchIndex != PatchUtil::invalidPatchIndex)
263+
radiance += PatchUtil::EvalIrradiance(patchIrradiances[bouncePatchIndex], normal);
266264
}
267265

268266
radiance *= albedo * INV_PI;
@@ -304,7 +302,7 @@ float3 IncomingEnviromentAndDirectionalBounceAndMultiBounceRadiance(
304302
const float3 hitAlbedo = MaterialPool::LoadAlbedoWithBoost(matEntry, matPoolParams.albedoTextures, matPoolParams.albedoSampler, matPoolParams.atlasTexelSize, matPoolParams.albedoBoost, hitGeo.uv0, hitGeo.uv1);
305303
const float3 hitEmission = MaterialPool::LoadEmission(matEntry, matPoolParams.emissionTextures, matPoolParams.emissionSampler, matPoolParams.atlasTexelSize, hitGeo.uv0, hitGeo.uv1);
306304

307-
bool multiBounceSurfaceCacheMiss = false;
305+
uint bouncePatchIndex;
308306
radiance = OutgoingDirectionalBounceAndMultiBounceRadiance(
309307
hitGeo.position,
310308
hitGeo.normal,
@@ -318,20 +316,32 @@ float3 IncomingEnviromentAndDirectionalBounceAndMultiBounceRadiance(
318316
volumeParams,
319317
hitAlbedo,
320318
hitEmission,
321-
multiBounceSurfaceCacheMiss);
319+
bouncePatchIndex);
322320

323321
#if BOUNCE_PATCH_ALLOCATION
324-
if (multiBounceSurfaceCacheMiss && enablePatchAllocation)
322+
if (enablePatchAllocation)
325323
{
326-
PatchUtil::AllocatePatch(
327-
hitGeo.position,
328-
hitGeo.normal,
329-
patchIrradiances,
330-
patchGeometries,
331-
patchStatistics,
332-
allocParams,
333-
volumeParams,
334-
frameIndex);
324+
if (bouncePatchIndex == PatchUtil::invalidPatchIndex)
325+
{
326+
PatchUtil::AllocatePatch(
327+
hitGeo.position,
328+
hitGeo.normal,
329+
patchIrradiances,
330+
patchGeometries,
331+
patchStatistics,
332+
allocParams,
333+
volumeParams,
334+
frameIndex);
335+
}
336+
else
337+
{
338+
PatchUtil::PatchCounterSet counters = patchStatistics[bouncePatchIndex].counters;
339+
if (PatchUtil::GetRank(counters) == 1)
340+
{
341+
PatchUtil::SetLastAccessFrame(counters, frameIndex);
342+
patchStatistics[bouncePatchIndex].counters = counters;
343+
}
344+
}
335345
}
336346
#endif
337347
}

0 commit comments

Comments
 (0)