Skip to content

Commit bc8d986

Browse files
Optimize GI data layout (#45)
* Optimized the layout of the dynamic GI data. * Added migration of existing baked data to new layout. Context menu item "Reserialize (All)" on Probe Volume Asset now also runs the migration. Had to move the migration code from Probe Volume to Probe Volume Asset for this. Limitation: Context menu migration is skipped for assets of version < 3, since updating to v.3 requires rotation from an object in a scene. * Apply inverse-square law to propagation axis weights to get a more correct distribution for diagonal axes.
1 parent 87a44f6 commit bc8d986

File tree

7 files changed

+189
-141
lines changed

7 files changed

+189
-141
lines changed

com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/DynamicGI/ProbePropagation.hlsl

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@
55
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/DynamicGI/ProbePropagationGlobals.hlsl"
66
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/DynamicGI/ProbeVolumeDynamicGI.hlsl"
77

8+
int _ProbeVolumeProbeCount;
9+
810
RWStructuredBuffer<float3> _RadianceCacheAxis;
911
StructuredBuffer<float3> _PreviousRadianceCacheAxis;
10-
int _RadianceCacheAxisCount;
1112

1213
int _ProbeVolumeIndex;
1314
float _LeakMultiplier;
@@ -21,11 +22,8 @@ bool IsFarFromCamera(float3 worldPosition, float rangeInFrontOfCamera, float ran
2122

2223
float3 ReadPreviousPropagationAxis(uint probeIndex, uint axisIndex)
2324
{
24-
const uint index = probeIndex * NEIGHBOR_AXIS_COUNT + axisIndex;
25-
26-
// TODO: remove this if check with stricter checks on construction side in C#
27-
return (index < (uint)_RadianceCacheAxisCount) ? _PreviousRadianceCacheAxis[index] : 0;
28-
25+
const uint index = axisIndex * _ProbeVolumeProbeCount + probeIndex;
26+
return _PreviousRadianceCacheAxis[index];
2927
}
3028

3129
float3 NormalizeOutputRadiance(float4 lightingAndWeight, float probeValidity)
@@ -39,16 +37,10 @@ float3 NormalizeOutputRadiance(float4 lightingAndWeight, float probeValidity)
3937
return radiance;
4038
}
4139

42-
void WritePropagationOutput(uint probeIndex, uint axisIndex, float4 lightingAndWeight, float probeValidity)
40+
void WritePropagationOutput(uint index, float4 lightingAndWeight, float probeValidity)
4341
{
44-
const uint index = probeIndex * NEIGHBOR_AXIS_COUNT + axisIndex;
45-
46-
// TODO: remove this if check with stricter checks on construction side in C#
47-
if(index < (uint)_RadianceCacheAxisCount)
48-
{
49-
const float3 finalRadiance = NormalizeOutputRadiance(lightingAndWeight, probeValidity);
50-
_RadianceCacheAxis[index] = finalRadiance;
51-
}
42+
const float3 finalRadiance = NormalizeOutputRadiance(lightingAndWeight, probeValidity);
43+
_RadianceCacheAxis[index] = finalRadiance;
5244
}
5345

5446

com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/DynamicGI/ProbePropagationAxes.compute

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -225,8 +225,8 @@ void PropagateLight(uint3 id : SV_DispatchThreadID)
225225
const int index = id.x;
226226
if (index < _ProbeVolumeNeighborsCount)
227227
{
228-
const uint probeIndex = (uint)index / NEIGHBOR_AXIS_COUNT;
229-
const uint axisIndex = (uint)index - probeIndex * NEIGHBOR_AXIS_COUNT;
228+
const uint axisIndex = (uint)index / _ProbeVolumeProbeCount;
229+
const uint probeIndex = (uint)index - axisIndex * _ProbeVolumeProbeCount;
230230

231231
float4 lightingAndWeight = float4(0, 0, 0, 1);
232232
uint3 probeCoordinate = ProbeIndexToProbeCoordinatesUint(probeIndex);
@@ -237,6 +237,12 @@ void PropagateLight(uint3 id : SV_DispatchThreadID)
237237
// Early out at far distances
238238
if (IsFarFromCamera(probePositionWS, _RangeInFrontOfCamera, _RangeBehindCamera))
239239
{
240+
#if defined(PREVIOUS_RADIANCE_CACHE_INVALID)
241+
_RadianceCacheAxis[index] = 0;
242+
#else
243+
_RadianceCacheAxis[index] = _PreviousRadianceCacheAxis[index];
244+
#endif
245+
240246
return;
241247
}
242248

@@ -263,7 +269,7 @@ void PropagateLight(uint3 id : SV_DispatchThreadID)
263269
float sgWeight = neighborAxisLookup.x;
264270

265271
float3 neighborDirection = _RayAxis[i].xyz;
266-
uint sampleAxis = probeIndex * NEIGHBOR_AXIS_COUNT + i;
272+
uint sampleAxis = i * _ProbeVolumeProbeCount + probeIndex;
267273
NeighborAxis neighbor = _ProbeVolumeNeighbors[sampleAxis];
268274

269275
uint hitIndex = 0;
@@ -322,6 +328,6 @@ void PropagateLight(uint3 id : SV_DispatchThreadID)
322328
probeValidity *= 1.0 / PROPAGATION_AXIS_AMOUNT;
323329

324330
lightingAndWeight.rgb += incomingRadiance * _PropagationContribution;
325-
WritePropagationOutput(probeIndex, axisIndex, lightingAndWeight, probeValidity);
331+
WritePropagationOutput(index, lightingAndWeight, probeValidity);
326332
}
327333
}

com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/DynamicGI/ProbePropagationCombine.compute

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -127,14 +127,14 @@ SHIncomingIrradiance ProjectPropagationAxisFromPeak(uint probeIndex)
127127
SHIncomingIrradiance incomingIrradiance;
128128
ZERO_INITIALIZE(SHIncomingIrradiance, incomingIrradiance);
129129

130-
uint localIndex = probeIndex * NEIGHBOR_AXIS_COUNT;
130+
uint localIndex = probeIndex;
131131
for (int axis = 0; axis < NEIGHBOR_AXIS_COUNT; ++axis)
132132
{
133133
float3 radiance = _RadianceCacheAxis[localIndex].xyz;
134134
float3 direction = _RayAxis[axis].xyz;
135135
SHIncomingIrradianceAccumulate(incomingIrradiance, direction, radiance);
136136
//FCCAddToOutputRepresentation(radiance, direction, incomingIrradiance);
137-
localIndex++;
137+
localIndex += _ProbeVolumeAtlasReadBufferCount;
138138
}
139139

140140
return incomingIrradiance;
@@ -213,7 +213,7 @@ SHIncomingIrradiance ProjectPropagationAxisFromFit(uint probeIndex)
213213
SHIncomingIrradiance incomingIrradiance;
214214
ZERO_INITIALIZE(SHIncomingIrradiance, incomingIrradiance);
215215

216-
uint localIndex = probeIndex * NEIGHBOR_AXIS_COUNT;
216+
uint localIndex = probeIndex;
217217
for (int axis = 0; axis < NEIGHBOR_AXIS_COUNT; ++axis)
218218
{
219219
float3 radiance = _RadianceCacheAxis[localIndex].xyz;
@@ -223,7 +223,7 @@ SHIncomingIrradiance ProjectPropagationAxisFromFit(uint probeIndex)
223223

224224
SHIncomingIrradianceAccumulateFromSHIncomingIrradiance(incomingIrradiance, incomingIrradianceCurrentSG);
225225

226-
localIndex++;
226+
localIndex += _ProbeVolumeAtlasReadBufferCount;
227227
}
228228

229229
return incomingIrradiance;

com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/DynamicGI/ProbeVolumeDynamicGI.cs

Lines changed: 34 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,6 @@ public partial class ProbeVolumeDynamicGI
7373
private ComputeShader _PropagationAxesShader = null;
7474
private ComputeShader _PropagationCombineShader = null;
7575

76-
private AxisVector4[] _tempAxisLookups;
7776
private Vector4[] _sortedAxisLookups;
7877
private ProbeVolumeSimulationRequest[] _probeVolumeSimulationRequests;
7978

@@ -131,7 +130,6 @@ public enum PropagationAxisAmount
131130
new Vector4( 0, -s_2DDiagonal, -s_2DDiagonal, s_2DDiagonalDist),
132131
};
133132

134-
_tempAxisLookups = new AxisVector4[s_NeighborAxis.Length];
135133
_sortedAxisLookups = new Vector4[s_NeighborAxis.Length * s_NeighborAxis.Length];
136134
_probeVolumeSimulationRequests = new ProbeVolumeSimulationRequest[MAX_SIMULATIONS_PER_FRAME];
137135
}
@@ -252,16 +250,16 @@ internal static void Copy(ref ProbeVolumePayload payloadSrc, ref ProbeVolumePayl
252250
}
253251
}
254252

255-
internal static void SetNeighborDataHit(ref ProbeVolumePayload payload, Vector3 albedo, Vector3 normal, float distance, float validity, int probeIndex, int axis, int neighborIndex, float maxDensity)
253+
internal static void SetNeighborDataHit(ref ProbeVolumePayload payload, Vector3 albedo, Vector3 normal, float distance, float validity, int probeIndex, int axis, int hitIndex, float maxDensity)
256254
{
257-
ref var neighborData = ref payload.hitNeighborAxis;
258-
if (neighborIndex >= neighborData.Length)
255+
ref var neighborDataHits = ref payload.hitNeighborAxis;
256+
if (hitIndex >= neighborDataHits.Length)
259257
{
260258
Debug.Assert(false, "Probe Volume Neighbor Indexing Code Error");
261259
return;
262260
}
263261

264-
neighborData[neighborIndex] = new PackedNeighborHit
262+
neighborDataHits[hitIndex] = new PackedNeighborHit
265263
{
266264
indexValidity = PackIndexAndValidity((uint) probeIndex, (uint) axis, validity),
267265
albedoDistance = PackAlbedoAndDistance(albedo, distance, maxDensity * s_DiagonalDist),
@@ -271,8 +269,9 @@ internal static void SetNeighborDataHit(ref ProbeVolumePayload payload, Vector3
271269

272270
private static void SetNeighborData(ref ProbeVolumePayload payload, float validity, int probeIndex, int axis, uint hitIndexOrMiss)
273271
{
274-
var axisIndex = probeIndex * s_NeighborAxis.Length + axis;
275272
ref var neighborData = ref payload.neighborAxis;
273+
var probeCount = neighborData.Length / s_NeighborAxis.Length;
274+
var axisIndex = axis * probeCount + probeIndex;
276275
if (axisIndex >= neighborData.Length)
277276
{
278277
Debug.Assert(false, "Probe Volume Neighbor Indexing Code Error");
@@ -406,9 +405,13 @@ internal void DispatchProbePropagation(CommandBuffer cmd, ProbeVolumeHandle prob
406405
var previousRadianceCacheInvalid = InitializePropagationBuffers(probeVolume);
407406

408407
DispatchClearPreviousRadianceCache(cmd, probeVolume, previousRadianceCacheInvalid || giSettings.clear.value || _clearAllActive);
409-
DispatchPropagationHits(cmd, probeVolume, in giSettings, previousRadianceCacheInvalid);
410-
DispatchPropagationAxes(cmd, probeVolume, in giSettings, previousRadianceCacheInvalid);
411-
DispatchPropagationCombine(cmd, probeVolume, in giSettings, in shaderGlobals, probeVolumeAtlasSHRTHandle);
408+
409+
using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.ProbeVolumeDynamicGIHits)))
410+
DispatchPropagationHits(cmd, probeVolume, in giSettings, previousRadianceCacheInvalid);
411+
using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.ProbeVolumeDynamicGIAxes)))
412+
DispatchPropagationAxes(cmd, probeVolume, in giSettings, previousRadianceCacheInvalid);
413+
using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.ProbeVolumeDynamicGICombine)))
414+
DispatchPropagationCombine(cmd, probeVolume, in giSettings, in shaderGlobals, probeVolumeAtlasSHRTHandle);
412415

413416
_stats.Simulated(probeVolume);
414417
probeVolume.propagationBuffers.SwapRadianceCaches();
@@ -431,18 +434,21 @@ void DispatchClearPreviousRadianceCache(CommandBuffer cmd, ProbeVolumeHandle pro
431434
{
432435
if (previousRadianceCacheInvalid)
433436
{
434-
var kernel = _PropagationClearRadianceShader.FindKernel("ClearPreviousRadianceCache");
435-
var shader = _PropagationClearRadianceShader;
436-
437-
cmd.SetComputeBufferParam(shader, kernel, "_RadianceCacheAxis0", probeVolume.propagationBuffers.radianceCacheAxis0);
438-
cmd.SetComputeBufferParam(shader, kernel, "_RadianceCacheAxis1", probeVolume.propagationBuffers.radianceCacheAxis1);
439-
cmd.SetComputeIntParam(shader, "_RadianceCacheAxisCount", probeVolume.propagationBuffers.radianceCacheAxis0.count);
440-
cmd.SetComputeBufferParam(shader, kernel, "_HitRadianceCacheAxis", probeVolume.propagationBuffers.hitRadianceCache);
441-
cmd.SetComputeIntParam(shader, "_HitRadianceCacheAxisCount", probeVolume.propagationBuffers.hitRadianceCache.count);
442-
443-
int numHits = Mathf.Max(probeVolume.propagationBuffers.radianceCacheAxis0.count, probeVolume.propagationBuffers.hitRadianceCache.count);
444-
int dispatchX = (numHits + 63) / 64;
445-
cmd.DispatchCompute(shader, kernel, dispatchX, 1, 1);
437+
using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.ProbeVolumeDynamicGIClear)))
438+
{
439+
var kernel = _PropagationClearRadianceShader.FindKernel("ClearPreviousRadianceCache");
440+
var shader = _PropagationClearRadianceShader;
441+
442+
cmd.SetComputeBufferParam(shader, kernel, "_RadianceCacheAxis0", probeVolume.propagationBuffers.radianceCacheAxis0);
443+
cmd.SetComputeBufferParam(shader, kernel, "_RadianceCacheAxis1", probeVolume.propagationBuffers.radianceCacheAxis1);
444+
cmd.SetComputeIntParam(shader, "_RadianceCacheAxisCount", probeVolume.propagationBuffers.radianceCacheAxis0.count);
445+
cmd.SetComputeBufferParam(shader, kernel, "_HitRadianceCacheAxis", probeVolume.propagationBuffers.hitRadianceCache);
446+
cmd.SetComputeIntParam(shader, "_HitRadianceCacheAxisCount", probeVolume.propagationBuffers.hitRadianceCache.count);
447+
448+
int numHits = Mathf.Max(probeVolume.propagationBuffers.radianceCacheAxis0.count, probeVolume.propagationBuffers.hitRadianceCache.count);
449+
int dispatchX = (numHits + 63) / 64;
450+
cmd.DispatchCompute(shader, kernel, dispatchX, 1, 1);
451+
}
446452
}
447453
}
448454

@@ -555,6 +561,7 @@ void DispatchPropagationAxes(CommandBuffer cmd, ProbeVolumeHandle probeVolume, i
555561

556562
cmd.SetComputeBufferParam(shader, kernel, "_ProbeVolumeNeighbors", probeVolume.propagationBuffers.neighbors);
557563
cmd.SetComputeIntParam(shader, "_ProbeVolumeNeighborsCount", probeVolume.propagationBuffers.neighbors.count);
564+
cmd.SetComputeIntParam(shader, "_ProbeVolumeProbeCount", probeVolume.propagationBuffers.neighbors.count / s_NeighborAxis.Length);
558565
cmd.SetComputeFloatParam(shader, "_LeakMultiplier", giSettings.leakMultiplier.value);
559566
cmd.SetComputeFloatParam(shader, "_PropagationContribution", giSettings.propagationContribution.value);
560567
cmd.SetComputeFloatParam(shader, "_PropagationSharpness", giSettings.propagationSharpness.value);
@@ -568,7 +575,6 @@ void DispatchPropagationAxes(CommandBuffer cmd, ProbeVolumeHandle probeVolume, i
568575

569576
cmd.SetComputeBufferParam(shader, kernel, "_PreviousRadianceCacheAxis", probeVolume.propagationBuffers.GetReadRadianceCacheAxis());
570577
cmd.SetComputeBufferParam(shader, kernel, "_RadianceCacheAxis", probeVolume.propagationBuffers.GetWriteRadianceCacheAxis());
571-
cmd.SetComputeIntParam(shader, "_RadianceCacheAxisCount", probeVolume.propagationBuffers.radianceCacheAxis0.count);
572578

573579
PrecomputeAxisCacheLookup(giSettings.propagationSharpness.value);
574580
cmd.SetComputeVectorArrayParam(shader, "_SortedNeighborAxis", _sortedAxisLookups);
@@ -709,20 +715,18 @@ unsafe void PrecomputeAxisCacheLookup(float sgSharpness)
709715
for (int axisIndex = 0; axisIndex < s_NeighborAxis.Length; ++axisIndex)
710716
{
711717
var axis = s_NeighborAxis[axisIndex];
718+
var sortedAxisStart = axisIndex * s_NeighborAxis.Length;
712719
for (int neighborIndex = 0; neighborIndex < s_NeighborAxis.Length; ++neighborIndex)
713720
{
714721
var neighborDirection = s_NeighborAxis[neighborIndex];
715722
var sgWeight = SGEvaluateFromDirection(1, sgSharpness, neighborDirection, axis);
716-
_tempAxisLookups[neighborIndex] = new AxisVector4(new Vector4(sgWeight, neighborIndex, 0, 0));
723+
sgWeight /= neighborDirection.w * neighborDirection.w;
724+
_sortedAxisLookups[sortedAxisStart + neighborIndex] = new Vector4(sgWeight, neighborIndex, 0, 0);
717725
}
718726

719-
fixed (AxisVector4* axisPtr = &_tempAxisLookups[0])
720-
{
721-
CoreUnsafeUtils.QuickSort<AxisVector4>(_tempAxisLookups.Length, axisPtr);
722-
}
723-
fixed (Vector4* sortedAxisPtr = &_sortedAxisLookups[axisIndex * s_NeighborAxis.Length])
727+
fixed (Vector4* sortedAxisPtr = &_sortedAxisLookups[sortedAxisStart])
724728
{
725-
CoreUnsafeUtils.CopyTo(_tempAxisLookups, sortedAxisPtr, s_NeighborAxis.Length);
729+
CoreUnsafeUtils.QuickSort<AxisVector4>(s_NeighborAxis.Length, sortedAxisPtr);
726730
}
727731
}
728732

com.unity.render-pipelines.high-definition/Runtime/Lighting/ProbeVolume/ProbeVolume.cs

Lines changed: 2 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -893,96 +893,10 @@ private Quaternion GetAssetRotation()
893893
return probeVolumeAsset.rotation;
894894
}
895895

896-
bool CheckMigrationRequirement()
897-
{
898-
if (probeVolumeAsset == null) return false;
899-
if (probeVolumeAsset.Version == (int)ProbeVolumeAsset.AssetVersion.Current) return false;
900-
return true;
901-
}
902-
903896
void Migrate()
904897
{
905-
// Must not be called at deserialization time if require other component
906-
while (CheckMigrationRequirement())
907-
{
908-
ApplyMigration();
909-
}
910-
}
911-
912-
void ApplyMigration()
913-
{
914-
switch ((ProbeVolumeAsset.AssetVersion)probeVolumeAsset.Version)
915-
{
916-
case ProbeVolumeAsset.AssetVersion.First:
917-
ApplyMigrationAddProbeVolumesAtlasEncodingModes();
918-
break;
919-
920-
case ProbeVolumeAsset.AssetVersion.AddProbeVolumesAtlasEncodingModes:
921-
ApplyMigrationAddOctahedralDepthVarianceFromLightmapper();
922-
break;
923-
case ProbeVolumeAsset.AssetVersion.AddOctahedralDepthVarianceFromLightmapper:
924-
ApplyMigrationAddRotation();
925-
break;
926-
default:
927-
// No migration required.
928-
break;
929-
}
930-
}
931-
932-
void ApplyMigrationAddProbeVolumesAtlasEncodingModes()
933-
{
934-
Debug.Assert(probeVolumeAsset != null && probeVolumeAsset.Version == (int)ProbeVolumeAsset.AssetVersion.First);
935-
936-
probeVolumeAsset.m_Version = (int)ProbeVolumeAsset.AssetVersion.AddProbeVolumesAtlasEncodingModes;
937-
938-
int probeLength = probeVolumeAsset.dataSH.Length;
939-
940-
ProbeVolumePayload.Allocate(ref probeVolumeAsset.payload, probeLength);
941-
942-
for (int i = 0; i < probeLength; ++i)
943-
{
944-
ProbeVolumePayload.SetSphericalHarmonicsL1FromIndex(ref probeVolumeAsset.payload, probeVolumeAsset.dataSH[i], i);
945-
}
946-
947-
probeVolumeAsset.dataSH = null;
948-
probeVolumeAsset.dataValidity = null;
949-
probeVolumeAsset.dataOctahedralDepth = null;
950-
}
951-
952-
void ApplyMigrationAddOctahedralDepthVarianceFromLightmapper()
953-
{
954-
Debug.Assert(probeVolumeAsset != null && probeVolumeAsset.Version == (int)ProbeVolumeAsset.AssetVersion.AddProbeVolumesAtlasEncodingModes);
955-
956-
probeVolumeAsset.m_Version = (int)ProbeVolumeAsset.AssetVersion.AddOctahedralDepthVarianceFromLightmapper;
957-
958-
if (probeVolumeAsset.payload.dataOctahedralDepth == null) { return; }
959-
960-
int probeLength = ProbeVolumePayload.GetLength(ref probeVolumeAsset.payload);
961-
var dataOctahedralDepthMigrated = new float[probeLength * ProbeVolumePayload.GetDataOctahedralDepthStride()];
962-
963-
// Previously, the lightmapper only returned scalar mean depth values for octahedralDepth.
964-
// Now it returns float2(depthMean, depthMean^2) which can be used to reconstruct a variance estimate.
965-
int dataOctahedralDepthLengthPrevious = probeVolumeAsset.payload.dataOctahedralDepth.Length;
966-
for (int i = 0; i < dataOctahedralDepthLengthPrevious; ++i)
967-
{
968-
float depthMean = probeVolumeAsset.payload.dataOctahedralDepth[i];
969-
970-
// For our migration, simply initialize our depthMeanSquared slots with depthMean * depthMean, which will reconstruct a zero variance estimate.
971-
// Really, the user will want to rebake to get a real variance estimate. This migration just ensures we do not error out.
972-
float depthMeanSquared = depthMean * depthMean;
973-
dataOctahedralDepthMigrated[i * 2 + 0] = depthMean;
974-
dataOctahedralDepthMigrated[i * 2 + 1] = depthMeanSquared;
975-
}
976-
probeVolumeAsset.payload.dataOctahedralDepth = dataOctahedralDepthMigrated;
977-
}
978-
979-
void ApplyMigrationAddRotation()
980-
{
981-
Debug.Assert(probeVolumeAsset != null && probeVolumeAsset.Version == (int)ProbeVolumeAsset.AssetVersion.AddOctahedralDepthVarianceFromLightmapper);
982-
983-
probeVolumeAsset.m_Version = (int)ProbeVolumeAsset.AssetVersion.AddRotation;
984-
985-
probeVolumeAsset.rotation = transform.rotation;
898+
if (probeVolumeAsset != null)
899+
probeVolumeAsset.Migrate(this);
986900
}
987901

988902
protected void OnEnable()

0 commit comments

Comments
 (0)