Rasagar/Library/PackageCache/com.unity.render-pipelines.high-definition/Runtime/Lighting/Light/HDProcessedVisibleLightsBuilder.Jobs.cs
2024-08-26 23:07:20 +03:00

388 lines
18 KiB
C#

using System;
using UnityEngine.Jobs;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
using System.Threading;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Burst.CompilerServices;
namespace UnityEngine.Rendering.HighDefinition
{
internal partial class HDProcessedVisibleLightsBuilder
{
JobHandle m_ProcessVisibleLightJobHandle;
#if ENABLE_BURST_1_5_0_OR_NEWER
[Unity.Burst.BurstCompile]
#endif
struct ProcessVisibleLightJob : IJobParallelFor
{
#region Light entity data
[NativeDisableContainerSafetyRestriction]
public NativeArray<HDLightRenderData> lightData;
#endregion
#region Visible light data
[ReadOnly]
public NativeArray<VisibleLight> visibleLights;
[ReadOnly]
public NativeArray<int> visibleLightEntityDataIndices;
[ReadOnly]
public NativeArray<LightBakingOutput> visibleLightBakingOutput;
[ReadOnly]
public NativeArray<LightShadows> visibleLightShadows;
#endregion
#region Parameters
[ReadOnly]
public int totalLightCounts;
[ReadOnly]
public float3 cameraPosition;
[ReadOnly]
public int pixelCount;
[ReadOnly]
public bool enableAreaLights;
[ReadOnly]
public bool enableRayTracing;
[ReadOnly]
public bool enablePathTracing;
[ReadOnly]
public bool showDirectionalLight;
[ReadOnly]
public bool showPunctualLight;
[ReadOnly]
public bool showAreaLight;
[ReadOnly]
public bool enableShadowMaps;
[ReadOnly]
public bool enableScreenSpaceShadows;
[ReadOnly]
public int maxDirectionalLightsOnScreen;
[ReadOnly]
public int maxPunctualLightsOnScreen;
[ReadOnly]
public int maxAreaLightsOnScreen;
[ReadOnly]
public DebugLightFilterMode debugFilterMode;
#endregion
#region output processed lights
[WriteOnly]
public NativeArray<int> processedVisibleLightCountsPtr;
[WriteOnly]
public NativeArray<LightVolumeType> processedLightVolumeType;
[WriteOnly]
public NativeArray<HDProcessedVisibleLight> processedEntities;
[WriteOnly]
[NativeDisableContainerSafetyRestriction]
public NativeArray<uint> sortKeys;
[WriteOnly]
[NativeDisableContainerSafetyRestriction]
public NativeArray<int> shadowLightsDataIndices;
#endregion
private bool TrivialRejectLight(in VisibleLight light, int dataIndex)
{
if (dataIndex < 0)
return true;
// If the light was forced visible it will be outside of the view and have no pixels on screen.
// The light will still generate a cached shadow map, but removed before going in to the
// main light loop.
if (light.forcedVisible)
return false;
// We can skip the processing of lights that are so small to not affect at least a pixel on screen.
// TODO: The minimum pixel size on screen should really be exposed as parameter, to allow small lights to be culled to user's taste.
const int minimumPixelAreaOnScreen = 1;
return (light.screenRect.height * light.screenRect.width * pixelCount) < minimumPixelAreaOnScreen;
}
private int IncrementCounter(HDProcessedVisibleLightsBuilder.ProcessLightsCountSlots counterSlot)
{
int outputIndex = 0;
unsafe
{
int* ptr = (int*)processedVisibleLightCountsPtr.GetUnsafePtr<int>() + (int)counterSlot;
outputIndex = Interlocked.Increment(ref UnsafeUtility.AsRef<int>(ptr));
}
return outputIndex;
}
private int DecrementCounter(HDProcessedVisibleLightsBuilder.ProcessLightsCountSlots counterSlot)
{
int outputIndex = 0;
unsafe
{
int* ptr = (int*)processedVisibleLightCountsPtr.GetUnsafePtr<int>() + (int)counterSlot;
outputIndex = Interlocked.Decrement(ref UnsafeUtility.AsRef<int>(ptr));
}
return outputIndex;
}
private int NextOutputIndex() => IncrementCounter(HDProcessedVisibleLightsBuilder.ProcessLightsCountSlots.ProcessedLights) - 1;
private bool IncrementLightCounterAndTestLimit(LightCategory lightCategory, GPULightType gpuLightType)
{
// Do NOT process lights beyond the specified limit!
switch (lightCategory)
{
case LightCategory.Punctual:
if (gpuLightType == GPULightType.Directional) // Our directional lights are "punctual"...
{
var directionalLightcount = IncrementCounter(HDProcessedVisibleLightsBuilder.ProcessLightsCountSlots.DirectionalLights) - 1;
if (!showDirectionalLight || directionalLightcount >= maxDirectionalLightsOnScreen)
{
DecrementCounter(HDProcessedVisibleLightsBuilder.ProcessLightsCountSlots.DirectionalLights);
return false;
}
break;
}
var punctualLightcount = IncrementCounter(HDProcessedVisibleLightsBuilder.ProcessLightsCountSlots.PunctualLights) - 1;
if (!showPunctualLight || punctualLightcount >= maxPunctualLightsOnScreen)
{
DecrementCounter(HDProcessedVisibleLightsBuilder.ProcessLightsCountSlots.PunctualLights);
return false;
}
break;
case LightCategory.Area:
var areaLightCount = IncrementCounter(HDProcessedVisibleLightsBuilder.ProcessLightsCountSlots.AreaLightCounts) - 1;
if (!showAreaLight || areaLightCount >= maxAreaLightsOnScreen)
{
DecrementCounter(HDProcessedVisibleLightsBuilder.ProcessLightsCountSlots.AreaLightCounts);
return false;
}
break;
default:
break;
}
return true;
}
private HDProcessedVisibleLightsBuilder.ShadowMapFlags EvaluateShadowState(
LightShadows shadows,
LightType lightType,
GPULightType gpuLightType,
bool useScreenSpaceShadowsVal,
bool useRayTracingShadowsVal,
float shadowDimmerVal,
float shadowFadeDistanceVal,
float distanceToCamera,
LightVolumeType lightVolumeType)
{
var flags = HDProcessedVisibleLightsBuilder.ShadowMapFlags.None;
bool willRenderShadowMap = shadows != LightShadows.None && enableShadowMaps;
if (!willRenderShadowMap)
return flags;
// When creating a new light, at the first frame, there is no AdditionalShadowData so we can't really render shadows
if (shadowDimmerVal <= 0)
return flags;
// If the shadow is too far away, we don't render it
bool isShadowInRange = lightType == LightType.Directional || distanceToCamera < shadowFadeDistanceVal;
if (!isShadowInRange)
return flags;
if (lightType == LightType.Tube || lightType == LightType.Disc)
return flags;
// First we reset the ray tracing and screen space shadow data
flags |= HDProcessedVisibleLightsBuilder.ShadowMapFlags.WillRenderShadowMap;
// If this camera does not allow screen space shadows we are done, set the target parameters to false and leave the function
if (!enableScreenSpaceShadows)
return flags;
// Flag the ray tracing only shadows
if (enableRayTracing && useRayTracingShadowsVal)
{
bool validShadow = false;
if (gpuLightType == GPULightType.Point
|| gpuLightType == GPULightType.Rectangle
|| gpuLightType == GPULightType.Spot
|| gpuLightType == GPULightType.ProjectorPyramid
|| gpuLightType == GPULightType.ProjectorBox)
validShadow = true;
if (validShadow)
flags |= HDProcessedVisibleLightsBuilder.ShadowMapFlags.WillRenderScreenSpaceShadow
| HDProcessedVisibleLightsBuilder.ShadowMapFlags.WillRenderRayTracedShadow;
}
// Flag the directional shadow
if (useScreenSpaceShadowsVal && gpuLightType == GPULightType.Directional)
{
flags |= HDProcessedVisibleLightsBuilder.ShadowMapFlags.WillRenderScreenSpaceShadow;
if (enableRayTracing && useRayTracingShadowsVal)
flags |= HDProcessedVisibleLightsBuilder.ShadowMapFlags.WillRenderRayTracedShadow;
}
return flags;
}
#if DEBUG
[IgnoreWarning(1370)] //Ignore throwing exception warning.
#endif
private ref HDLightRenderData GetLightData(int dataIndex)
{
#if DEBUG
if (dataIndex < 0 || dataIndex >= totalLightCounts)
throw new Exception("Trying to access a light from the DB out of bounds. The index requested is: " + dataIndex + " and the length is " + totalLightCounts);
#endif
unsafe
{
HDLightRenderData* data = (HDLightRenderData*)lightData.GetUnsafePtr<HDLightRenderData>() + dataIndex;
return ref UnsafeUtility.AsRef<HDLightRenderData>(data);
}
}
#if DEBUG
[IgnoreWarning(1370)] //Ignore throwing exception warning.
#endif
public void Execute(int index)
{
VisibleLight visibleLight = visibleLights[index];
int dataIndex = visibleLightEntityDataIndices[index];
LightBakingOutput bakingOutput = visibleLightBakingOutput[index];
LightShadows shadows = visibleLightShadows[index];
if (TrivialRejectLight(visibleLight, dataIndex))
return;
ref HDLightRenderData lightRenderData = ref GetLightData(dataIndex);
if (enableRayTracing && !enablePathTracing && !lightRenderData.includeForRayTracing)
return;
if (enableRayTracing && enablePathTracing && !lightRenderData.includeForPathTracing)
return;
float3 lightPosition = visibleLight.GetPosition();
float distanceToCamera = math.distance(cameraPosition, lightPosition);
var lightType = visibleLight.lightType;
var lightCategory = LightCategory.Count;
var gpuLightType = GPULightType.Point;
if (!enableAreaLights && (lightType == LightType.Rectangle || lightType == LightType.Tube))
return;
var lightVolumeType = LightVolumeType.Count;
var isBakedShadowMaskLight =
bakingOutput.lightmapBakeType == LightmapBakeType.Mixed &&
bakingOutput.mixedLightingMode == MixedLightingMode.Shadowmask &&
bakingOutput.occlusionMaskChannel != -1; // We need to have an occlusion mask channel assign, else we have no shadow mask
HDRenderPipeline.EvaluateGPULightType(lightType, ref lightCategory, ref gpuLightType, ref lightVolumeType);
if (debugFilterMode != DebugLightFilterMode.None && debugFilterMode.IsEnabledFor(gpuLightType))
return;
float lightDistanceFade = gpuLightType == GPULightType.Directional ? 1.0f : HDUtils.ComputeLinearDistanceFade(distanceToCamera, lightRenderData.fadeDistance);
float volumetricDistanceFade = gpuLightType == GPULightType.Directional ? 1.0f : HDUtils.ComputeLinearDistanceFade(distanceToCamera, lightRenderData.volumetricFadeDistance);
bool contributesToLighting = ((lightRenderData.lightDimmer > 0) && (lightRenderData.affectDiffuse || lightRenderData.affectSpecular)) || ((lightRenderData.affectVolumetric ? lightRenderData.volumetricDimmer : 0.0f) > 0);
contributesToLighting = contributesToLighting && (lightDistanceFade > 0);
var shadowMapFlags = EvaluateShadowState(
shadows, lightType, gpuLightType, lightRenderData.useScreenSpaceShadows,
lightRenderData.useRayTracedShadows, lightRenderData.shadowDimmer,
lightRenderData.shadowFadeDistance, distanceToCamera, lightVolumeType);
if (!contributesToLighting)
return;
if (!IncrementLightCounterAndTestLimit(lightCategory, gpuLightType))
return;
int outputIndex = NextOutputIndex();
#if DEBUG
if (outputIndex < 0 || outputIndex >= visibleLights.Length)
throw new Exception("Trying to access an output index out of bounds. Output index is " + outputIndex + "and max length is " + visibleLights.Length);
#endif
sortKeys[outputIndex] = HDGpuLightsBuilder.PackLightSortKey(lightCategory, gpuLightType, lightVolumeType, index, visibleLight.forcedVisible);
processedLightVolumeType[index] = lightVolumeType;
processedEntities[index] = new HDProcessedVisibleLight()
{
dataIndex = dataIndex,
gpuLightType = gpuLightType,
lightType = lightType,
lightDistanceFade = lightDistanceFade,
lightVolumetricDistanceFade = volumetricDistanceFade,
distanceToCamera = distanceToCamera,
shadowMapFlags = shadowMapFlags,
isBakedShadowMask = isBakedShadowMaskLight
};
if (isBakedShadowMaskLight)
IncrementCounter(HDProcessedVisibleLightsBuilder.ProcessLightsCountSlots.BakedShadows);
if ((shadowMapFlags & HDProcessedVisibleLightsBuilder.ShadowMapFlags.WillRenderShadowMap) != 0)
{
int shadowOutputIndex = IncrementCounter(HDProcessedVisibleLightsBuilder.ProcessLightsCountSlots.ShadowLights) - 1;
shadowLightsDataIndices[shadowOutputIndex] = index;
}
}
}
public void StartProcessVisibleLightJob(
HDCamera hdCamera,
bool rayTracingState,
NativeArray<VisibleLight> visibleLights,
in GlobalLightLoopSettings lightLoopSettings,
DebugDisplaySettings debugDisplaySettings)
{
if (m_Size == 0)
return;
var lightEntityCollection = HDLightRenderDatabase.instance;
var processVisibleLightJob = new ProcessVisibleLightJob()
{
//Parameters.
totalLightCounts = lightEntityCollection.lightCount,
cameraPosition = hdCamera.camera.transform.position,
pixelCount = hdCamera.actualWidth * hdCamera.actualHeight,
enableAreaLights = ShaderConfig.s_AreaLights != 0,
enableRayTracing = hdCamera.frameSettings.IsEnabled(FrameSettingsField.RayTracing) && rayTracingState,
enablePathTracing = hdCamera.IsPathTracingEnabled() && rayTracingState,
showDirectionalLight = debugDisplaySettings.data.lightingDebugSettings.showDirectionalLight,
showPunctualLight = debugDisplaySettings.data.lightingDebugSettings.showPunctualLight,
showAreaLight = debugDisplaySettings.data.lightingDebugSettings.showAreaLight,
enableShadowMaps = hdCamera.frameSettings.IsEnabled(FrameSettingsField.ShadowMaps),
enableScreenSpaceShadows = hdCamera.frameSettings.IsEnabled(FrameSettingsField.ScreenSpaceShadows),
maxDirectionalLightsOnScreen = lightLoopSettings.maxDirectionalLightsOnScreen,
maxPunctualLightsOnScreen = lightLoopSettings.maxPunctualLightsOnScreen,
maxAreaLightsOnScreen = lightLoopSettings.maxAreaLightsOnScreen,
debugFilterMode = debugDisplaySettings.GetDebugLightFilterMode(),
//render light entities.
lightData = lightEntityCollection.lightData,
//data of all visible light entities.
visibleLights = visibleLights,
visibleLightEntityDataIndices = m_VisibleLightEntityDataIndices,
visibleLightBakingOutput = m_VisibleLightBakingOutput,
visibleLightShadows = m_VisibleLightShadows,
//Output processed lights.
processedVisibleLightCountsPtr = m_ProcessVisibleLightCounts,
processedLightVolumeType = m_ProcessedLightVolumeType,
processedEntities = m_ProcessedEntities,
sortKeys = m_SortKeys,
shadowLightsDataIndices = m_ShadowLightsDataIndices
};
m_ProcessVisibleLightJobHandle = processVisibleLightJob.Schedule(m_Size, 32);
}
public void CompleteProcessVisibleLightJob()
{
if (m_Size == 0)
return;
m_ProcessVisibleLightJobHandle.Complete();
}
}
}