Skip to content

Commit

Permalink
Parallel LightLoop fixes and improvements (#74)
Browse files Browse the repository at this point in the history
* Native collection leak fix

* IsFromVisibleList improvement

Add null guards to HDProcessedVisibleLightsBuilder light count accessor functions. m_ProcessVisibleLightCounts is not allocated until BuildVisibleLightEntities(), but the counts can be queried outside of the lightloop, before build - in particular inside of HDAdditionalLightData::WillRenderShadowMap() (#77)

Fixed rejecting of hidden lights in the light list. (#78)

dont skip if shadow update mode is on demand (#81)

Process both Visible and Dynamic GI lights at the same time (#82)

* Process both Visible and Dynamic GI lights at the same time

* Removed (unused) directional DGI lights

* Removed (unused) LightVolumeType array

Consider all offscreen lights when building DGI light data (#86)

* Consider all offscreen lights when building DGI light data

* Prefilter offscreen lights to only process DGI-enabled ones

* Requested changes from PR comments

* Improved duplicate DGI light detection

* Removed some GC allocations

* Keep a list of DGI-enabled lights so we don't have to check every frame

* Bugfix: allDGIEnabledLights isn't a superset of cullResults.visibleLights anymore

* Keep the list of DGI lights from HDAdditionalLightData sorted so it's even cheaper to find duplicates

Handle case when no valid shadow casters overlap a given shadow casting light. If we do not early out unity throws the error: No valid shadow casters (#90)

No valid shadow casters error fix (#91)

* Fix GetShadowCasterBounds check
  • Loading branch information
pastasfuture committed Oct 21, 2022
1 parent b51f98c commit 465f3b0
Show file tree
Hide file tree
Showing 10 changed files with 529 additions and 445 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1204,6 +1204,21 @@ public bool affectDynamicGI
m_AffectDynamicGI = value;
if (lightEntity.valid)
HDLightRenderDatabase.instance.EditLightDataAsRef(lightEntity).affectDynamicGI = m_AffectDynamicGI;

if (m_AffectDynamicGI)
{
int index = s_InstancesHDAdditionalLightData.IndexOf(this);
if (index != -1)
{
var light = this.GetComponent<Light>();
s_DynamicGILights.Add((uint)light.GetInstanceID(), light);
}
}
else
{
var light = this.GetComponent<Light>();
s_DynamicGILights.Remove((uint)light.GetInstanceID());
}
}
}

Expand Down Expand Up @@ -3629,11 +3644,19 @@ internal bool IsOverlapping()
// custom-begin:
[System.NonSerialized] public static List<HDAdditionalLightData> s_InstancesHDAdditionalLightData = new List<HDAdditionalLightData>();
[System.NonSerialized] public static List<Light> s_InstancesLight = new List<Light>();
[System.NonSerialized] public static SortedList<uint, Light> s_DynamicGILights = new SortedList<uint, Light>();

private void InstanceAdd()
{
var light = this.GetComponent<Light>();

s_InstancesHDAdditionalLightData.Add(this);
s_InstancesLight.Add(this.GetComponent<Light>());
s_InstancesLight.Add(light);

if (affectDynamicGI)
{
s_DynamicGILights.Add((uint)light.GetInstanceID(), light);
}
}

private void InstanceRemove()
Expand All @@ -3644,6 +3667,9 @@ private void InstanceRemove()
s_InstancesHDAdditionalLightData.RemoveAt(index);
s_InstancesLight.RemoveAt(index);
}

var light = this.GetComponent<Light>();
s_DynamicGILights.Remove((uint)light.GetInstanceID());
}

public static List<Light> GetLightInstances()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,14 @@ internal struct CreateGpuLightDataJob : IJobParallelFor
[ReadOnly]
public int outputDirectionalLightCounts;
[ReadOnly]
public int outputDGILightCounts;
[ReadOnly]
public int outputLightBoundsCount;
[ReadOnly]
public CreateGpuLightDataJobGlobalConfig globalConfig;
[ReadOnly]
public Vector3 cameraPos;
[ReadOnly]
public int directionalSortedLightCounts;
[ReadOnly]
public bool isPbrSkyActive;
[ReadOnly]
public int precomputedAtmosphericAttenuation;
Expand All @@ -62,12 +62,6 @@ internal struct CreateGpuLightDataJob : IJobParallelFor
public int viewCounts;
[ReadOnly]
public bool useCameraRelativePosition;
[ReadOnly]
public bool skipDirectionalLights;
[ReadOnly]
public bool modulateByBounceIntensity;
[ReadOnly]
public bool computeLightDataVolumeAndBound;

//sky settings
[ReadOnly]
Expand All @@ -93,11 +87,13 @@ internal struct CreateGpuLightDataJob : IJobParallelFor
[ReadOnly]
public NativeArray<uint> sortKeys;
[ReadOnly]
public NativeArray<uint> dgiSortKeys;
[ReadOnly]
public NativeArray<HDProcessedVisibleLight> processedEntities;
[ReadOnly]
public NativeArray<VisibleLight> visibleLights;
[ReadOnly]
public NativeArray<VisibleLight> visibleOffscreenLights;
public NativeArray<VisibleLight> offscreenDGILights;
[ReadOnly]
public NativeArray<LightBakingOutput> visibleLightBakingOutput;
[ReadOnly]
Expand All @@ -115,6 +111,9 @@ internal struct CreateGpuLightDataJob : IJobParallelFor
public NativeArray<DirectionalLightData> directionalLights;
[WriteOnly]
[NativeDisableContainerSafetyRestriction]
public NativeArray<LightData> dgiLights;
[WriteOnly]
[NativeDisableContainerSafetyRestriction]
public NativeArray<LightsPerView> lightsPerView;
[WriteOnly]
[NativeDisableContainerSafetyRestriction]
Expand All @@ -125,6 +124,9 @@ internal struct CreateGpuLightDataJob : IJobParallelFor
[WriteOnly]
[NativeDisableContainerSafetyRestriction]
public NativeArray<int> gpuLightCounters;
[WriteOnly]
[NativeDisableContainerSafetyRestriction]
public NativeArray<int> dgiGpuLightCounters;
#endregion

private ref HDLightRenderData GetLightData(int dataIndex)
Expand All @@ -149,11 +151,11 @@ private static uint GetLightLayer(bool lightLayersEnabled, in HDLightRenderData

private static Vector3 GetLightColor(in VisibleLight light) => new Vector3(light.finalColor.r, light.finalColor.g, light.finalColor.b);

private void IncrementCounter(HDGpuLightsBuilder.GPULightTypeCountSlots counterSlot)
private void IncrementCounter(ref NativeArray<int> counterSet, HDGpuLightsBuilder.GPULightTypeCountSlots counterSlot)
{
unsafe
{
int* ptr = (int*)gpuLightCounters.GetUnsafePtr<int>() + (int)counterSlot;
int* ptr = (int*)counterSet.GetUnsafePtr<int>() + (int)counterSlot;
Interlocked.Increment(ref UnsafeUtility.AsRef<int>(ptr));
}
}
Expand Down Expand Up @@ -346,16 +348,17 @@ private void IncrementCounter(HDGpuLightsBuilder.GPULightTypeCountSlots counterS

private void StoreAndConvertLightToGPUFormat(
int outputIndex, int lightIndex,
LightCategory lightCategory, GPULightType gpuLightType, LightVolumeType lightVolumeType)
LightCategory lightCategory, GPULightType gpuLightType, LightVolumeType lightVolumeType,
bool isDGI)
{
bool isFromVisibleList = lightIndex < visibleLights.Length;
var light = isFromVisibleList ? visibleLights[lightIndex] : visibleOffscreenLights[lightIndex - visibleLights.Length];
var light = isFromVisibleList ? visibleLights[lightIndex] : offscreenDGILights[lightIndex - visibleLights.Length];
var processedEntity = processedEntities[lightIndex];
var lightData = new LightData();
ref HDLightRenderData lightRenderData = ref GetLightData(processedEntity.dataIndex);

ConvertLightToGPUFormat(
lightCategory, gpuLightType, globalConfig,
lightCategory, gpuLightType, globalConfig,
visibleLightShadowCasterMode[lightIndex],
visibleLightBakingOutput[lightIndex],
light,
Expand All @@ -364,12 +367,14 @@ private void IncrementCounter(HDGpuLightsBuilder.GPULightTypeCountSlots counterS
out var lightDimensions,
ref lightData);

if (modulateByBounceIntensity)
// DGI lights get modulated by light bounce intensity
if (isDGI)
{
lightData.color *= visibleLightBounceIntensity[lightIndex];
}

if (computeLightDataVolumeAndBound)
// Volume and Bound data is only calculated for visible (non-DGI) lights
if (!isDGI)
{
for (int viewId = 0; viewId < viewCounts; ++viewId)
{
Expand All @@ -383,24 +388,28 @@ private void IncrementCounter(HDGpuLightsBuilder.GPULightTypeCountSlots counterS
if (useCameraRelativePosition)
lightData.positionRWS -= cameraPos;

var counterSet = !isDGI ? gpuLightCounters : dgiGpuLightCounters;
int maxLightCount = !isDGI ? outputLightCounts : outputDGILightCounts;
var outputArray = !isDGI ? lights : dgiLights;

switch (lightCategory)
{
case LightCategory.Punctual:
IncrementCounter(HDGpuLightsBuilder.GPULightTypeCountSlots.Punctual);
IncrementCounter(ref counterSet, HDGpuLightsBuilder.GPULightTypeCountSlots.Punctual);
break;
case LightCategory.Area:
IncrementCounter(HDGpuLightsBuilder.GPULightTypeCountSlots.Area);
IncrementCounter(ref counterSet, HDGpuLightsBuilder.GPULightTypeCountSlots.Area);
break;
default:
Debug.Assert(false, "TODO: encountered an unknown LightCategory.");
break;
}

#if DEBUG
if (outputIndex < 0 || outputIndex >= outputLightCounts)
if (outputIndex < 0 || outputIndex >= maxLightCount)
throw new Exception("Trying to access an output index out of bounds. Output index is " + outputIndex + "and max length is " + outputLightCounts);
#endif
lights[outputIndex] = lightData;
outputArray[outputIndex] = lightData;
}

private void ComputeLightVolumeDataAndBound(
Expand Down Expand Up @@ -681,33 +690,40 @@ private void IncrementCounter(HDGpuLightsBuilder.GPULightTypeCountSlots counterS
if (useCameraRelativePosition)
lightData.positionRWS -= cameraPos;

IncrementCounter(HDGpuLightsBuilder.GPULightTypeCountSlots.Directional);
IncrementCounter(ref gpuLightCounters, HDGpuLightsBuilder.GPULightTypeCountSlots.Directional);

#if DEBUG
if (outputIndex < 0 || outputIndex >= outputDirectionalLightCounts)
throw new Exception("Trying to access an output index out of bounds. Output index is " + outputIndex + "and max length is " + outputLightCounts);
throw new Exception("Trying to access an output index out of bounds. Output index is " + outputIndex + "and max length is " + outputDirectionalLightCounts);
#endif

directionalLights[outputIndex] = lightData;
}

public void Execute(int index)
{
var sortKey = sortKeys[index];
int totalVisibleLights = outputLightCounts + outputDirectionalLightCounts;

bool isDGI = index >= totalVisibleLights;
int localIndex = !isDGI ? index : (index - totalVisibleLights);

var sortKey = !isDGI ? sortKeys[localIndex] : dgiSortKeys[localIndex];
HDGpuLightsBuilder.UnpackLightSortKey(sortKey, out var lightCategory, out var gpuLightType, out var lightVolumeType, out var lightIndex);

if (gpuLightType == GPULightType.Directional)
{
if (!skipDirectionalLights)
if (!isDGI) // While we setup output buffers for directional DGI lights, they're never used, so we skip processing them
{
int outputIndex = index;
int outputIndex = localIndex;
ConvertDirectionalLightToGPUFormat(outputIndex, lightIndex, lightCategory, gpuLightType, lightVolumeType);
}
}
else
{
int outputIndex = index - directionalSortedLightCounts;
StoreAndConvertLightToGPUFormat(outputIndex, lightIndex, lightCategory, gpuLightType, lightVolumeType);
int outputIndex = localIndex;
if (!isDGI)
outputIndex = localIndex - outputDirectionalLightCounts;
StoreAndConvertLightToGPUFormat(outputIndex, lightIndex, lightCategory, gpuLightType, lightVolumeType, isDGI);
}
}
}
Expand All @@ -730,16 +746,15 @@ public void Execute(int index)
totalLightCounts = lightEntities.lightCount,
outputLightCounts = m_LightCount,
outputDirectionalLightCounts = m_DirectionalLightCount,
outputDGILightCounts = m_DGILightCount,
outputLightBoundsCount = m_LightBoundsCount,
globalConfig = CreateGpuLightDataJobGlobalConfig.Create(hdCamera, hdShadowSettings),
cameraPos = hdCamera.camera.transform.position,
directionalSortedLightCounts = visibleLights.sortedDirectionalLightCounts,
isPbrSkyActive = isPbrSkyActive,
precomputedAtmosphericAttenuation = ShaderConfig.s_PrecomputedAtmosphericAttenuation,
defaultDataIndex = lightEntities.GetEntityDataIndex(lightEntities.GetDefaultLightEntity()),
viewCounts = hdCamera.viewCount,
useCameraRelativePosition = ShaderConfig.s_CameraRelativeRendering != 0,
skipDirectionalLights = SkipDirectionalLights,

planetCenterPosition = skySettings.GetPlanetCenterPosition(hdCamera.camera.transform.position),
planetaryRadius = skySettings.GetPlanetaryRadius(),
Expand All @@ -753,49 +768,32 @@ public void Execute(int index)

//visible lights processed
sortKeys = visibleLights.sortKeys,
dgiSortKeys = visibleLights.sortKeysDGI,
processedEntities = visibleLights.processedEntities,
visibleLights = cullingResult.visibleLights,
visibleOffscreenLights = cullingResult.visibleOffscreenVertexLights,
offscreenDGILights = visibleLights.offscreenDynamicGILights,
visibleLightBakingOutput = visibleLights.visibleLightBakingOutput,
visibleLightShadowCasterMode = visibleLights.visibleLightShadowCasterMode,
visibleLightBounceIntensity = visibleLights.visibleLightBounceIntensity,

//outputs
gpuLightCounters = m_LightTypeCounters,
dgiGpuLightCounters = m_DGILightTypeCounters,
lights = m_Lights,
directionalLights = m_DirectionalLights,
dgiLights = m_DGILights,
lightsPerView = m_LightsPerView,
lightBounds = m_LightBounds,
lightVolumes = m_LightVolumes
};
OverrideCreateGpuLightDataJobParameters(ref createGpuLightDataJob);

m_CreateGpuLightDataJobHandle = createGpuLightDataJob.Schedule(visibleLights.sortedLightCounts, 32);
m_CreateGpuLightDataJobHandle = createGpuLightDataJob.Schedule(visibleLights.sortedLightCounts + visibleLights.sortedDGILightCounts, 32);
}

public void CompleteGpuLightDataJob()
{
m_CreateGpuLightDataJobHandle.Complete();
}

protected abstract void OverrideCreateGpuLightDataJobParameters(ref CreateGpuLightDataJob job);
}

internal partial class HDGpuLightsRegularBuilder : HDGpuLightsBuilder
{
protected override void OverrideCreateGpuLightDataJobParameters(ref CreateGpuLightDataJob job)
{
job.modulateByBounceIntensity = false;
job.computeLightDataVolumeAndBound = true;
}
}

internal partial class HDGpuLightsDynamicBuilder : HDGpuLightsBuilder
{
protected override void OverrideCreateGpuLightDataJobParameters(ref CreateGpuLightDataJob job)
{
job.modulateByBounceIntensity = true;
job.computeLightDataVolumeAndBound = false;
}
}
}

0 comments on commit 465f3b0

Please sign in to comment.