You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
977 lines
60 KiB
977 lines
60 KiB
using System;
|
|
using Unity.Burst;
|
|
using Unity.Collections;
|
|
using Unity.Collections.LowLevel.Unsafe;
|
|
using Unity.Jobs;
|
|
using Unity.Mathematics;
|
|
using Unity.Profiling;
|
|
|
|
namespace UnityEngine.Rendering.HighDefinition
|
|
{
|
|
static class HDShadowCullingUtils
|
|
{
|
|
static class ComputeShadowCullingInfoProfilerMarkers
|
|
{
|
|
public static ProfilerMarker indicesAndPreambleMarker = new ProfilerMarker("WriteShadowIndicesAndCollectLightInfo");
|
|
public static ProfilerMarker lightBucketingMarker = new ProfilerMarker("LightBucketing");
|
|
public static ProfilerMarker computePointShadowCullingInfosMarker = new ProfilerMarker("ComputePointShadowCullingInfos");
|
|
public static ProfilerMarker computeSpotShadowCullingInfosMarker = new ProfilerMarker("ComputeSpotShadowCullingInfos");
|
|
public static ProfilerMarker computeAreaRectangleShadowCullingInfosMarker = new ProfilerMarker("ComputeAreaRectangleShadowCullingInfos");
|
|
public static ProfilerMarker computeDirectionalShadowCullingInfosMarker = new ProfilerMarker("ComputeDirectionalShadowCullingInfos");
|
|
}
|
|
|
|
public static unsafe void ComputeCullingSplits(in HDShadowInitParameters hdShadowInitParams,
|
|
HDLightRenderDatabase lightRenderDatabase,
|
|
HDShadowRequestDatabase shadowRequestDatabase,
|
|
HDShadowManager shadowManager,
|
|
HDShadowSettings shadowSettings,
|
|
in CullingResults cullingResult,
|
|
HDProcessedVisibleLightsBuilder processedVisibleLights,
|
|
NativeArray<LightShadowCasterCullingInfo> outPerLightShadowCullingInfos,
|
|
NativeArray<ShadowSplitData> outSplitBuffer,
|
|
out int outTotalSplitCount)
|
|
{
|
|
using var profilerScope = new ProfilingScope(ProfilingSampler.Get(HDProfileId.ComputeShadowCullingSplits));
|
|
|
|
HDShadowManagerDataForComputeCullingSplitsJob shadowManagerData = default;
|
|
shadowManager.GetUnmanageDataForComputeCullingSplitsJob(ref shadowManagerData);
|
|
|
|
int shadowLightCount = processedVisibleLights.shadowLightCount;
|
|
int maxShadowSplitCount = shadowLightCount * HDShadowUtils.k_MaxShadowSplitCount;
|
|
int visibleLightCount = cullingResult.visibleLights.Length;
|
|
NativeArray<HDProcessedVisibleLight> processedLights = processedVisibleLights.processedEntities.GetSubArray(0, visibleLightCount);
|
|
NativeArray<int> splitBufferOffset = new NativeArray<int>(1, Allocator.TempJob);
|
|
NativeArray<Matrix4x4> cubemapFaces = new NativeArray<Matrix4x4>(HDShadowUtils.kCubemapFaces, Allocator.TempJob);
|
|
|
|
NativeReference<float3> newCachedDirectionalAngles = new NativeReference<float3>(HDCachedShadowManager.instance.cachedDirectionalAngles, Allocator.TempJob);
|
|
NativeArray<HDShadowCullingSplit> hdSplitBuffer = processedVisibleLights.shadowCullingSplitBuffer.GetSubArray(0, maxShadowSplitCount);
|
|
NativeArray<ShadowIndicesAndVisibleLightData> visibleLightsAndIndicesBuffer = processedVisibleLights.visibleLightsAndIndicesBuffer;
|
|
NativeList<ShadowIndicesAndVisibleLightData> splitVisibleLightsAndIndicesBuffer = processedVisibleLights.splitVisibleLightsAndIndicesBuffer;
|
|
|
|
// Those lists are output by the Burst job. They will alias the splitVisibleLightsAndIndicesBuffer above so they must no be disposed.
|
|
UnsafeList<ShadowIndicesAndVisibleLightData> dynamicPointVisibleLightsAndIndices;
|
|
UnsafeList<ShadowIndicesAndVisibleLightData> cachedPointVisibleLightsAndIndices;
|
|
UnsafeList<ShadowIndicesAndVisibleLightData> dynamicSpotVisibleLightsAndIndices;
|
|
UnsafeList<ShadowIndicesAndVisibleLightData> cachedSpotVisibleLightsAndIndices;
|
|
UnsafeList<ShadowIndicesAndVisibleLightData> dynamicAreaRectangleVisibleLightsAndIndices;
|
|
UnsafeList<ShadowIndicesAndVisibleLightData> cachedAreaRectangleVisibleLightsAndIndices;
|
|
UnsafeList<ShadowIndicesAndVisibleLightData> dynamicDirectionalVisibleLightsAndIndices;
|
|
UnsafeList<ShadowIndicesAndVisibleLightData> cachedDirectionalVisibleLightsAndIndices;
|
|
|
|
// Those lists are output by the Burst job. They will alias the hdSplitBuffer above so they must not be disposed.
|
|
UnsafeList<HDShadowCullingSplit> dynamicPointHDSplits;
|
|
UnsafeList<HDShadowCullingSplit> cachedPointHDSplits;
|
|
UnsafeList<HDShadowCullingSplit> dynamicSpotHDSplits;
|
|
UnsafeList<HDShadowCullingSplit> cachedSpotHDSplits;
|
|
UnsafeList<HDShadowCullingSplit> dynamicAreaRectangleHDSplits;
|
|
UnsafeList<HDShadowCullingSplit> cachedAreaRectangleHDSplits;
|
|
UnsafeList<HDShadowCullingSplit> dynamicDirectionalHDSplits;
|
|
UnsafeList<HDShadowCullingSplit> cachedDirectionalHDSplits;
|
|
|
|
var computeShadowCasterCullingInfosJob = new ComputeShadowCasterCullingInfosJob
|
|
{
|
|
indicesAndPreambleMarker = ComputeShadowCullingInfoProfilerMarkers.indicesAndPreambleMarker,
|
|
lightBucketingMarker = ComputeShadowCullingInfoProfilerMarkers.lightBucketingMarker,
|
|
computePointShadowCullingInfosMarker = ComputeShadowCullingInfoProfilerMarkers.computePointShadowCullingInfosMarker,
|
|
computeSpotShadowCullingInfosMarker = ComputeShadowCullingInfoProfilerMarkers.computeSpotShadowCullingInfosMarker,
|
|
computeAreaRectangleShadowCullingInfosMarker = ComputeShadowCullingInfoProfilerMarkers.computeAreaRectangleShadowCullingInfosMarker,
|
|
computeDirectionalShadowCullingInfosMarker = ComputeShadowCullingInfoProfilerMarkers.computeDirectionalShadowCullingInfosMarker,
|
|
|
|
shadowManager = shadowManagerData,
|
|
cubeMapFaces = cubemapFaces,
|
|
visibleLights = cullingResult.visibleLights,
|
|
processedLights = processedLights,
|
|
sortKeys = processedVisibleLights.sortKeys,
|
|
visibleLightEntityDataIndices = processedVisibleLights.visibleLightEntityDataIndices,
|
|
additionalLightDataUpdateInfos = lightRenderDatabase.additionalLightDataUpdateInfos,
|
|
shadowResolutionRequestStorage = shadowManager.shadowResolutionRequestStorage.AsArray(),
|
|
packedShadowRequestSetHandles = lightRenderDatabase.packedShadowRequestSetHandles.AsArray(),
|
|
hdShadowRequestIndicesStorage = shadowRequestDatabase.hdShadowRequestIndicesStorage.AsArray(),
|
|
cascadeShadowSplits = GetCascadeRatiosAsVector3(shadowSettings.cascadeShadowSplits),
|
|
cascadeShadowSplitCount = shadowSettings.cascadeShadowSplitCount.value,
|
|
punctualShadowFilteringQuality = hdShadowInitParams.punctualShadowFilteringQuality,
|
|
usesReversedZBuffer = SystemInfo.usesReversedZBuffer,
|
|
shadowNearPlaneOffset = QualitySettings.shadowNearPlaneOffset,
|
|
sortedLightCount = processedVisibleLights.sortedLightCounts,
|
|
invalidDataIndex = HDLightRenderDatabase.InvalidDataIndex,
|
|
|
|
inOutSplitBufferOffset = splitBufferOffset,
|
|
outNewCachedDirectionalAngles = newCachedDirectionalAngles,
|
|
outShadowRequestValidityArray = processedVisibleLights.shadowRequestValidityArray,
|
|
outHDSplitBuffer = hdSplitBuffer,
|
|
outSplitBuffer = outSplitBuffer,
|
|
outPerLightShadowCullingInfos = outPerLightShadowCullingInfos,
|
|
outVisibleLightsAndIndicesBuffer = visibleLightsAndIndicesBuffer,
|
|
outSplitVisibleLightsAndIndicesBuffer = splitVisibleLightsAndIndicesBuffer,
|
|
|
|
outDynamicPointVisibleLightsAndIndices = &dynamicPointVisibleLightsAndIndices,
|
|
outCachedPointVisibleLightsAndIndices = &cachedPointVisibleLightsAndIndices,
|
|
outDynamicSpotVisibleLightsAndIndices = &dynamicSpotVisibleLightsAndIndices,
|
|
outCachedSpotVisibleLightsAndIndices = &cachedSpotVisibleLightsAndIndices,
|
|
outDynamicAreaRectangleVisibleLightsAndIndices = &dynamicAreaRectangleVisibleLightsAndIndices,
|
|
outCachedAreaRectangleVisibleLightsAndIndices = &cachedAreaRectangleVisibleLightsAndIndices,
|
|
outDynamicDirectionalVisibleLightsAndIndices = &dynamicDirectionalVisibleLightsAndIndices,
|
|
outCachedDirectionalVisibleLightsAndIndices = &cachedDirectionalVisibleLightsAndIndices,
|
|
|
|
outDynamicPointHDSplits = &dynamicPointHDSplits,
|
|
outCachedPointHDSplits = &cachedPointHDSplits,
|
|
outDynamicSpotHDSplits = &dynamicSpotHDSplits,
|
|
outCachedSpotHDSplits = &cachedSpotHDSplits,
|
|
outDynamicAreaRectangleHDSplits = &dynamicAreaRectangleHDSplits,
|
|
outCachedAreaRectangleHDSplits = &cachedAreaRectangleHDSplits,
|
|
outDynamicDirectionalHDSplits = &dynamicDirectionalHDSplits,
|
|
outCachedDirectionalHDSplits = &cachedDirectionalHDSplits
|
|
};
|
|
|
|
// This double pass setup is needed because we can not compute the ShadowSplitData for directional lights using Burst.
|
|
// We would need to call CullingResults.ComputeDirectionalShadowMatricesAndCullingPrimitives from a Burst job, but that is not possible at the moment.
|
|
// So the first pass is a Burst job processing all the lights except the directionals. It also output a LightMetadata struct to an UnsafeList for each "valid" directional light found.
|
|
// The second pass is then just normal C# code processing the UnsafeList of directional lights found by the Burst job.
|
|
computeShadowCasterCullingInfosJob.Run();
|
|
computeShadowCasterCullingInfosJob.ProcessDirectionalLights(cullingResult, dynamicDirectionalVisibleLightsAndIndices, cachedDirectionalVisibleLightsAndIndices);
|
|
outTotalSplitCount = splitBufferOffset[0];
|
|
|
|
processedVisibleLights.dynamicPointVisibleLightsAndIndices = dynamicPointVisibleLightsAndIndices;
|
|
processedVisibleLights.cachedPointVisibleLightsAndIndices = cachedPointVisibleLightsAndIndices;
|
|
processedVisibleLights.dynamicSpotVisibleLightsAndIndices = dynamicSpotVisibleLightsAndIndices;
|
|
processedVisibleLights.cachedSpotVisibleLightsAndIndices = cachedSpotVisibleLightsAndIndices;
|
|
processedVisibleLights.dynamicAreaRectangleVisibleLightsAndIndices = dynamicAreaRectangleVisibleLightsAndIndices;
|
|
processedVisibleLights.cachedAreaRectangleVisibleLightsAndIndices = cachedAreaRectangleVisibleLightsAndIndices;
|
|
processedVisibleLights.dynamicDirectionalVisibleLightsAndIndices = dynamicDirectionalVisibleLightsAndIndices;
|
|
processedVisibleLights.cachedDirectionalVisibleLightsAndIndices = cachedDirectionalVisibleLightsAndIndices;
|
|
|
|
processedVisibleLights.dynamicPointHDSplits = dynamicPointHDSplits;
|
|
processedVisibleLights.cachedPointHDSplits = cachedPointHDSplits;
|
|
processedVisibleLights.dynamicSpotHDSplits = dynamicSpotHDSplits;
|
|
processedVisibleLights.cachedSpotHDSplits = cachedSpotHDSplits;
|
|
processedVisibleLights.dynamicAreaRectangleHDSplits = dynamicAreaRectangleHDSplits;
|
|
processedVisibleLights.cachedAreaRectangleHDSplits = cachedAreaRectangleHDSplits;
|
|
processedVisibleLights.dynamicDirectionalHDSplits = dynamicDirectionalHDSplits;
|
|
processedVisibleLights.cachedDirectionalHDSplits = cachedDirectionalHDSplits;
|
|
|
|
HDCachedShadowManager.instance.cachedDirectionalAngles = newCachedDirectionalAngles.Value;
|
|
|
|
splitBufferOffset.Dispose();
|
|
cubemapFaces.Dispose();
|
|
newCachedDirectionalAngles.Dispose();
|
|
}
|
|
|
|
[BurstCompile]
|
|
unsafe struct ComputeShadowCasterCullingInfosJob : IJob
|
|
{
|
|
[ReadOnly] public ProfilerMarker indicesAndPreambleMarker;
|
|
[ReadOnly] public ProfilerMarker lightBucketingMarker;
|
|
[ReadOnly] public ProfilerMarker computePointShadowCullingInfosMarker;
|
|
[ReadOnly] public ProfilerMarker computeSpotShadowCullingInfosMarker;
|
|
[ReadOnly] public ProfilerMarker computeAreaRectangleShadowCullingInfosMarker;
|
|
[ReadOnly] public ProfilerMarker computeDirectionalShadowCullingInfosMarker;
|
|
|
|
[ReadOnly] public NativeArray<Matrix4x4> cubeMapFaces;
|
|
[ReadOnly] public NativeArray<VisibleLight> visibleLights;
|
|
[ReadOnly] public NativeArray<HDProcessedVisibleLight> processedLights;
|
|
[ReadOnly] public NativeArray<uint> sortKeys;
|
|
[ReadOnly] public NativeArray<int> visibleLightEntityDataIndices;
|
|
[ReadOnly] public NativeArray<HDAdditionalLightDataUpdateInfo> additionalLightDataUpdateInfos;
|
|
[ReadOnly] public NativeArray<HDShadowResolutionRequest> shadowResolutionRequestStorage;
|
|
[ReadOnly] public NativeArray<HDShadowRequestSetHandle> packedShadowRequestSetHandles;
|
|
[ReadOnly] public NativeArray<int> hdShadowRequestIndicesStorage;
|
|
[ReadOnly] public Vector3 cascadeShadowSplits;
|
|
[ReadOnly] public int cascadeShadowSplitCount;
|
|
[ReadOnly] public HDShadowFilteringQuality punctualShadowFilteringQuality;
|
|
[ReadOnly] public bool usesReversedZBuffer;
|
|
[ReadOnly] public float shadowNearPlaneOffset;
|
|
[ReadOnly] public int sortedLightCount;
|
|
[ReadOnly] public int invalidDataIndex;
|
|
|
|
public HDShadowManagerDataForComputeCullingSplitsJob shadowManager;
|
|
public NativeArray<int> inOutSplitBufferOffset;
|
|
public NativeReference<float3> outNewCachedDirectionalAngles;
|
|
public NativeBitArray outShadowRequestValidityArray;
|
|
public NativeArray<HDShadowCullingSplit> outHDSplitBuffer;
|
|
public NativeArray<ShadowSplitData> outSplitBuffer;
|
|
public NativeArray<LightShadowCasterCullingInfo> outPerLightShadowCullingInfos;
|
|
public NativeArray<ShadowIndicesAndVisibleLightData> outVisibleLightsAndIndicesBuffer;
|
|
public NativeList<ShadowIndicesAndVisibleLightData> outSplitVisibleLightsAndIndicesBuffer;
|
|
|
|
[NativeDisableUnsafePtrRestriction] public UnsafeList<ShadowIndicesAndVisibleLightData>* outDynamicPointVisibleLightsAndIndices;
|
|
[NativeDisableUnsafePtrRestriction] public UnsafeList<ShadowIndicesAndVisibleLightData>* outCachedPointVisibleLightsAndIndices;
|
|
[NativeDisableUnsafePtrRestriction] public UnsafeList<ShadowIndicesAndVisibleLightData>* outDynamicSpotVisibleLightsAndIndices;
|
|
[NativeDisableUnsafePtrRestriction] public UnsafeList<ShadowIndicesAndVisibleLightData>* outCachedSpotVisibleLightsAndIndices;
|
|
[NativeDisableUnsafePtrRestriction] public UnsafeList<ShadowIndicesAndVisibleLightData>* outDynamicAreaRectangleVisibleLightsAndIndices;
|
|
[NativeDisableUnsafePtrRestriction] public UnsafeList<ShadowIndicesAndVisibleLightData>* outCachedAreaRectangleVisibleLightsAndIndices;
|
|
[NativeDisableUnsafePtrRestriction] public UnsafeList<ShadowIndicesAndVisibleLightData>* outDynamicDirectionalVisibleLightsAndIndices;
|
|
[NativeDisableUnsafePtrRestriction] public UnsafeList<ShadowIndicesAndVisibleLightData>* outCachedDirectionalVisibleLightsAndIndices;
|
|
|
|
[NativeDisableUnsafePtrRestriction] public UnsafeList<HDShadowCullingSplit>* outDynamicPointHDSplits;
|
|
[NativeDisableUnsafePtrRestriction] public UnsafeList<HDShadowCullingSplit>* outCachedPointHDSplits;
|
|
[NativeDisableUnsafePtrRestriction] public UnsafeList<HDShadowCullingSplit>* outDynamicSpotHDSplits;
|
|
[NativeDisableUnsafePtrRestriction] public UnsafeList<HDShadowCullingSplit>* outCachedSpotHDSplits;
|
|
[NativeDisableUnsafePtrRestriction] public UnsafeList<HDShadowCullingSplit>* outDynamicAreaRectangleHDSplits;
|
|
[NativeDisableUnsafePtrRestriction] public UnsafeList<HDShadowCullingSplit>* outCachedAreaRectangleHDSplits;
|
|
[NativeDisableUnsafePtrRestriction] public UnsafeList<HDShadowCullingSplit>* outDynamicDirectionalHDSplits;
|
|
[NativeDisableUnsafePtrRestriction] public UnsafeList<HDShadowCullingSplit>* outCachedDirectionalHDSplits;
|
|
|
|
public void Execute()
|
|
{
|
|
HDAdditionalLightDataUpdateInfo* updateInfosUnsafePtr = (HDAdditionalLightDataUpdateInfo*)additionalLightDataUpdateInfos.GetUnsafeReadOnlyPtr();
|
|
ShadowIndicesAndVisibleLightData* visibleLightsAndIndicesBufferPtr = (ShadowIndicesAndVisibleLightData*)outVisibleLightsAndIndicesBuffer.GetUnsafePtr();
|
|
|
|
int cachedAreaRectangleCount = 0;
|
|
int cachedPointCount = 0;
|
|
int cachedSpotCount = 0;
|
|
int cachedDirectionalCount = 0;
|
|
int dynamicAreaRectangleCount = 0;
|
|
int dynamicPointCount = 0;
|
|
int dynamicSpotCount = 0;
|
|
int dynamicDirectionalCount = 0;
|
|
int collectedLightCount = 0;
|
|
|
|
// We do a few things in this first loop:
|
|
//
|
|
// 1. We gather counts for each light type/atlas combo so that we can categorically bucket them in the next loop.
|
|
// We do this to reduce the 30-odd atlas collections potentially involved with each light down to 9 or less per loop.
|
|
//
|
|
// 2. We copy relevant light data to a buffer, in order to reduce random access in subsequent loops.
|
|
//
|
|
|
|
using (indicesAndPreambleMarker.Auto())
|
|
{
|
|
float3 newCachedDirectionalAngles = shadowManager.cachedDirectionalAngles;
|
|
|
|
for (int sortKeyIndex = 0; sortKeyIndex < sortedLightCount; sortKeyIndex++)
|
|
{
|
|
uint sortKey = sortKeys[sortKeyIndex];
|
|
|
|
int lightIndex = (int)(sortKey & 0xFFFF);
|
|
if ((processedLights[lightIndex].shadowMapFlags & HDProcessedVisibleLightsBuilder.ShadowMapFlags.WillRenderShadowMap) == 0)
|
|
continue;
|
|
|
|
int dataIndex = visibleLightEntityDataIndices[lightIndex];
|
|
if (dataIndex == invalidDataIndex)
|
|
continue;
|
|
|
|
HDShadowRequestSetHandle shadowRequestSetHandle = packedShadowRequestSetHandles[dataIndex];
|
|
if (!shadowRequestSetHandle.valid)
|
|
continue;
|
|
|
|
ref HDAdditionalLightDataUpdateInfo lightUpdateInfo = ref updateInfosUnsafePtr[dataIndex];
|
|
VisibleLight visibleLight = visibleLights[lightIndex];
|
|
int splitCount = HDAdditionalLightData.GetShadowRequestCount(cascadeShadowSplitCount, visibleLight.lightType);
|
|
ShadowMapUpdateType shadowUpdateType = HDAdditionalLightData.GetShadowUpdateType(visibleLight.lightType,
|
|
lightUpdateInfo.shadowUpdateMode,
|
|
lightUpdateInfo.alwaysDrawDynamicShadows,
|
|
shadowManager.cachedShadowManager.directionalHasCachedAtlas);
|
|
|
|
BitArray8 isSplitValidMask = new BitArray8(0);
|
|
|
|
for (int i = 0; i < splitCount; i++)
|
|
{
|
|
HDShadowRequestHandle shadowRequestIndexLocation = shadowRequestSetHandle[i];
|
|
|
|
int shadowRequestIndex = hdShadowRequestIndicesStorage[shadowRequestIndexLocation.storageIndexForRequestIndex];
|
|
if (shadowRequestIndex < 0 || shadowRequestIndex >= shadowManager.requestCount)
|
|
continue;
|
|
|
|
isSplitValidMask[(uint)i] = true;
|
|
}
|
|
|
|
if (isSplitValidMask.allFalse)
|
|
continue;
|
|
|
|
BitArray8 needCacheUpdateMask = ComputeNeedCacheUpdateMask(ref lightUpdateInfo, ref visibleLight, splitCount, ref newCachedDirectionalAngles);
|
|
|
|
ref ShadowIndicesAndVisibleLightData bufferElement = ref visibleLightsAndIndicesBufferPtr[lightIndex];
|
|
bufferElement.additionalLightUpdateInfo = lightUpdateInfo;
|
|
bufferElement.visibleLight = visibleLight;
|
|
bufferElement.dataIndex = dataIndex;
|
|
bufferElement.lightIndex = lightIndex;
|
|
bufferElement.shadowRequestSetHandle = shadowRequestSetHandle;
|
|
bufferElement.sortKeyIndex = sortKeyIndex;
|
|
bufferElement.splitCount = splitCount;
|
|
bufferElement.isSplitValidMask = isSplitValidMask;
|
|
bufferElement.needCacheUpdateMask = needCacheUpdateMask;
|
|
bufferElement.shadowUpdateType = shadowUpdateType;
|
|
|
|
outShadowRequestValidityArray.Set(sortKeyIndex, true);
|
|
|
|
switch (visibleLight.lightType)
|
|
{
|
|
case LightType.Spot:
|
|
case LightType.Box:
|
|
case LightType.Pyramid:
|
|
if (lightUpdateInfo.hasCachedComponent)
|
|
++cachedSpotCount;
|
|
else
|
|
++dynamicSpotCount;
|
|
break;
|
|
case LightType.Directional:
|
|
if (lightUpdateInfo.hasCachedComponent)
|
|
++cachedDirectionalCount;
|
|
else
|
|
++dynamicDirectionalCount;
|
|
break;
|
|
case LightType.Point:
|
|
if (lightUpdateInfo.hasCachedComponent)
|
|
++cachedPointCount;
|
|
else
|
|
++dynamicPointCount;
|
|
break;
|
|
case LightType.Rectangle:
|
|
if (lightUpdateInfo.hasCachedComponent)
|
|
++cachedAreaRectangleCount;
|
|
else
|
|
++dynamicAreaRectangleCount;
|
|
break;
|
|
default:
|
|
throw new NotImplementedException("Light type not supported in real-time");
|
|
}
|
|
|
|
++collectedLightCount;
|
|
}
|
|
|
|
outNewCachedDirectionalAngles.Value = newCachedDirectionalAngles;
|
|
}
|
|
|
|
// Now that we have the counts for each bucket, we divide a scratchpad array into slices,
|
|
// and categorically sort data from our buffer into each slice of it.
|
|
// Future iterations may involve storing the data in a normalized form to begin with,
|
|
// in which case we would skip both this and the count collection from the previous loop.
|
|
|
|
outSplitVisibleLightsAndIndicesBuffer.Length = collectedLightCount;
|
|
ShadowIndicesAndVisibleLightData* splitVisibleLightsAndIndicesBufferPtr = (ShadowIndicesAndVisibleLightData*)outSplitVisibleLightsAndIndicesBuffer.GetUnsafePtr();
|
|
int bufferOffsetIterator = 0;
|
|
|
|
UnsafeList<ShadowIndicesAndVisibleLightData> dynamicPointVisibleLightsAndIndices = new UnsafeList<ShadowIndicesAndVisibleLightData>(splitVisibleLightsAndIndicesBufferPtr + bufferOffsetIterator, 0);
|
|
dynamicPointVisibleLightsAndIndices.m_capacity = dynamicPointCount;
|
|
bufferOffsetIterator += dynamicPointCount;
|
|
UnsafeList<ShadowIndicesAndVisibleLightData> cachedPointVisibleLightsAndIndices = new UnsafeList<ShadowIndicesAndVisibleLightData>(splitVisibleLightsAndIndicesBufferPtr + bufferOffsetIterator, 0);
|
|
cachedPointVisibleLightsAndIndices.m_capacity = cachedPointCount;
|
|
bufferOffsetIterator += cachedPointCount;
|
|
|
|
UnsafeList<ShadowIndicesAndVisibleLightData> dynamicSpotVisibleLightsAndIndices = new UnsafeList<ShadowIndicesAndVisibleLightData>(splitVisibleLightsAndIndicesBufferPtr + bufferOffsetIterator, 0);
|
|
dynamicSpotVisibleLightsAndIndices.m_capacity = dynamicSpotCount;
|
|
bufferOffsetIterator += dynamicSpotCount;
|
|
UnsafeList<ShadowIndicesAndVisibleLightData> cachedSpotVisibleLightsAndIndices = new UnsafeList<ShadowIndicesAndVisibleLightData>(splitVisibleLightsAndIndicesBufferPtr + bufferOffsetIterator, 0);
|
|
cachedSpotVisibleLightsAndIndices.m_capacity = cachedSpotCount;
|
|
bufferOffsetIterator += cachedSpotCount;
|
|
|
|
UnsafeList<ShadowIndicesAndVisibleLightData> dynamicAreaRectangleVisibleLightsAndIndices = new UnsafeList<ShadowIndicesAndVisibleLightData>(splitVisibleLightsAndIndicesBufferPtr + bufferOffsetIterator, 0);
|
|
dynamicAreaRectangleVisibleLightsAndIndices.m_capacity = dynamicAreaRectangleCount;
|
|
bufferOffsetIterator += dynamicAreaRectangleCount;
|
|
UnsafeList<ShadowIndicesAndVisibleLightData> cachedAreaRectangleVisibleLightsAndIndices = new UnsafeList<ShadowIndicesAndVisibleLightData>(splitVisibleLightsAndIndicesBufferPtr + bufferOffsetIterator, 0);
|
|
cachedAreaRectangleVisibleLightsAndIndices.m_capacity = cachedAreaRectangleCount;
|
|
bufferOffsetIterator += cachedAreaRectangleCount;
|
|
|
|
UnsafeList<ShadowIndicesAndVisibleLightData> dynamicDirectionalVisibleLightsAndIndices = new UnsafeList<ShadowIndicesAndVisibleLightData>(splitVisibleLightsAndIndicesBufferPtr + bufferOffsetIterator, 0);
|
|
dynamicDirectionalVisibleLightsAndIndices.m_capacity = dynamicDirectionalCount;
|
|
bufferOffsetIterator += dynamicDirectionalCount;
|
|
UnsafeList<ShadowIndicesAndVisibleLightData> cachedDirectionalVisibleLightsAndIndices = new UnsafeList<ShadowIndicesAndVisibleLightData>(splitVisibleLightsAndIndicesBufferPtr + bufferOffsetIterator, 0);
|
|
cachedDirectionalVisibleLightsAndIndices.m_capacity = cachedDirectionalCount;
|
|
bufferOffsetIterator += cachedDirectionalCount;
|
|
|
|
using (lightBucketingMarker.Auto())
|
|
{
|
|
for (int sortKeyIndex = 0; sortKeyIndex < sortedLightCount; sortKeyIndex++)
|
|
{
|
|
uint sortKey = sortKeys[sortKeyIndex];
|
|
if (!outShadowRequestValidityArray.IsSet(sortKeyIndex))
|
|
continue;
|
|
|
|
int lightIndex = (int)(sortKey & 0xFFFF);
|
|
ref ShadowIndicesAndVisibleLightData readData = ref visibleLightsAndIndicesBufferPtr[lightIndex];
|
|
ref HDAdditionalLightDataUpdateInfo lightUpdateInfo = ref readData.additionalLightUpdateInfo;
|
|
LightType lightType = readData.visibleLight.lightType;
|
|
bool hasCachedComponent = lightUpdateInfo.shadowUpdateMode != ShadowUpdateMode.EveryFrame;
|
|
|
|
switch (lightType)
|
|
{
|
|
case LightType.Spot:
|
|
case LightType.Pyramid:
|
|
case LightType.Box:
|
|
if (hasCachedComponent)
|
|
{
|
|
cachedSpotVisibleLightsAndIndices.AddNoResize(readData);
|
|
}
|
|
else
|
|
{
|
|
dynamicSpotVisibleLightsAndIndices.AddNoResize(readData);
|
|
}
|
|
break;
|
|
case LightType.Directional:
|
|
if (hasCachedComponent)
|
|
{
|
|
cachedDirectionalVisibleLightsAndIndices.AddNoResize(readData);
|
|
}
|
|
else
|
|
{
|
|
dynamicDirectionalVisibleLightsAndIndices.AddNoResize(readData);
|
|
}
|
|
break;
|
|
case LightType.Point:
|
|
if (hasCachedComponent)
|
|
{
|
|
cachedPointVisibleLightsAndIndices.AddNoResize(readData);
|
|
}
|
|
else
|
|
{
|
|
dynamicPointVisibleLightsAndIndices.AddNoResize(readData);
|
|
}
|
|
break;
|
|
case LightType.Rectangle:
|
|
if (hasCachedComponent)
|
|
{
|
|
cachedAreaRectangleVisibleLightsAndIndices.AddNoResize(readData);
|
|
}
|
|
else
|
|
{
|
|
dynamicAreaRectangleVisibleLightsAndIndices.AddNoResize(readData);
|
|
}
|
|
break;
|
|
default:
|
|
throw new NotImplementedException("Light type not supported in real-time");
|
|
}
|
|
}
|
|
}
|
|
|
|
int splitBufferOffset = inOutSplitBufferOffset[0];
|
|
HDShadowCullingSplit* hdSplitBufferPtr = (HDShadowCullingSplit*)outHDSplitBuffer.GetUnsafePtr();
|
|
|
|
UnsafeList<HDShadowCullingSplit> dynamicPointHDSplits;
|
|
UnsafeList<HDShadowCullingSplit> cachedPointHDSplits;
|
|
|
|
using (computePointShadowCullingInfosMarker.Auto())
|
|
{
|
|
int dynamicSplitOffset = splitBufferOffset;
|
|
int dynamicSplitCount = ComputePointShadowCullingSplits(dynamicPointVisibleLightsAndIndices, dynamicSplitOffset);
|
|
splitBufferOffset += dynamicSplitCount;
|
|
|
|
int cachedSplitOffset = splitBufferOffset;
|
|
int cachedSplitCount = ComputePointShadowCullingSplits(cachedPointVisibleLightsAndIndices, cachedSplitOffset);
|
|
splitBufferOffset += cachedSplitCount;
|
|
|
|
dynamicPointHDSplits = new UnsafeList<HDShadowCullingSplit>(hdSplitBufferPtr + dynamicSplitOffset, dynamicSplitCount);
|
|
cachedPointHDSplits = new UnsafeList<HDShadowCullingSplit>(hdSplitBufferPtr + cachedSplitOffset, cachedSplitCount);
|
|
}
|
|
|
|
UnsafeList<HDShadowCullingSplit> dynamicSpotHDSplits;
|
|
UnsafeList<HDShadowCullingSplit> cachedSpotHDSplits;
|
|
|
|
using (computeSpotShadowCullingInfosMarker.Auto())
|
|
{
|
|
int dynamicSplitOffset = splitBufferOffset;
|
|
int dynamicSplitCount = ComputeSpotShadowCullingSplits(dynamicSpotVisibleLightsAndIndices, dynamicSplitOffset);
|
|
splitBufferOffset += dynamicSplitCount;
|
|
|
|
int cachedSplitOffset = splitBufferOffset;
|
|
int cachedSplitCount = ComputeSpotShadowCullingSplits(cachedSpotVisibleLightsAndIndices, cachedSplitOffset);
|
|
splitBufferOffset += cachedSplitCount;
|
|
|
|
dynamicSpotHDSplits = new UnsafeList<HDShadowCullingSplit>(hdSplitBufferPtr + dynamicSplitOffset, dynamicSplitCount);
|
|
cachedSpotHDSplits = new UnsafeList<HDShadowCullingSplit>(hdSplitBufferPtr + cachedSplitOffset, cachedSplitCount);
|
|
}
|
|
|
|
UnsafeList<HDShadowCullingSplit> dynamicAreaRectangleHDSplits;
|
|
UnsafeList<HDShadowCullingSplit> cachedAreaRectangleHDSplits;
|
|
|
|
using (computeAreaRectangleShadowCullingInfosMarker.Auto())
|
|
{
|
|
int dynamicSplitOffset = splitBufferOffset;
|
|
int dynamicSplitCount = ComputeAreaRectangleShadowCullingSplits(dynamicAreaRectangleVisibleLightsAndIndices, dynamicSplitOffset);
|
|
splitBufferOffset += dynamicSplitCount;
|
|
|
|
int cachedSplitOffset = splitBufferOffset;
|
|
int cachedSplitCount = ComputeAreaRectangleShadowCullingSplits(cachedAreaRectangleVisibleLightsAndIndices, cachedSplitOffset);
|
|
splitBufferOffset += cachedSplitCount;
|
|
|
|
dynamicAreaRectangleHDSplits = new UnsafeList<HDShadowCullingSplit>(hdSplitBufferPtr + dynamicSplitOffset, dynamicSplitCount);
|
|
cachedAreaRectangleHDSplits = new UnsafeList<HDShadowCullingSplit>(hdSplitBufferPtr + cachedSplitOffset, cachedSplitCount);
|
|
}
|
|
|
|
inOutSplitBufferOffset[0] = splitBufferOffset;
|
|
|
|
*outDynamicPointVisibleLightsAndIndices = dynamicPointVisibleLightsAndIndices;
|
|
*outCachedPointVisibleLightsAndIndices = cachedPointVisibleLightsAndIndices;
|
|
*outDynamicSpotVisibleLightsAndIndices = dynamicSpotVisibleLightsAndIndices;
|
|
*outCachedSpotVisibleLightsAndIndices = cachedSpotVisibleLightsAndIndices;
|
|
*outDynamicAreaRectangleVisibleLightsAndIndices = dynamicAreaRectangleVisibleLightsAndIndices;
|
|
*outCachedAreaRectangleVisibleLightsAndIndices = cachedAreaRectangleVisibleLightsAndIndices;
|
|
*outDynamicDirectionalVisibleLightsAndIndices = dynamicDirectionalVisibleLightsAndIndices;
|
|
*outCachedDirectionalVisibleLightsAndIndices = cachedDirectionalVisibleLightsAndIndices;
|
|
|
|
*outDynamicPointHDSplits = dynamicPointHDSplits;
|
|
*outCachedPointHDSplits = cachedPointHDSplits;
|
|
*outDynamicSpotHDSplits = dynamicSpotHDSplits;
|
|
*outCachedSpotHDSplits = cachedSpotHDSplits;
|
|
*outDynamicAreaRectangleHDSplits = dynamicAreaRectangleHDSplits;
|
|
*outCachedAreaRectangleHDSplits = cachedAreaRectangleHDSplits;
|
|
}
|
|
|
|
int ComputeSpotShadowCullingSplits(UnsafeList<ShadowIndicesAndVisibleLightData> visibleLightsAndIndicesDatas, int initialSplitBufferOffset)
|
|
{
|
|
if (visibleLightsAndIndicesDatas.Length == 0)
|
|
return 0;
|
|
|
|
int lightSplitBufferOffset = initialSplitBufferOffset;
|
|
|
|
for (int i = 0; i < visibleLightsAndIndicesDatas.Length; i++)
|
|
{
|
|
ref ShadowIndicesAndVisibleLightData visibleLightsAndIndicesData = ref visibleLightsAndIndicesDatas.ElementAt(i);
|
|
ref VisibleLight visibleLight = ref visibleLightsAndIndicesData.visibleLight;
|
|
ref HDAdditionalLightDataUpdateInfo light = ref visibleLightsAndIndicesData.additionalLightUpdateInfo;
|
|
int lightIndex = visibleLightsAndIndicesData.lightIndex;
|
|
int shadowRequestIndicesBeginIndex = visibleLightsAndIndicesData.shadowRequestSetHandle.storageIndexForRequestIndices;
|
|
NativeArray<int> shadowRequestIndices = hdShadowRequestIndicesStorage.GetSubArray(shadowRequestIndicesBeginIndex, HDShadowRequest.maxLightShadowRequestsCount);
|
|
|
|
Debug.Assert(visibleLightsAndIndicesData.splitCount == 1);
|
|
|
|
bool skipCulling = true;
|
|
ShadowSplitData splitData = default;
|
|
HDShadowCullingSplit hdSplit = default;
|
|
hdSplit.splitIndex = 0;
|
|
|
|
if (visibleLightsAndIndicesData.isSplitValidMask[0])
|
|
{
|
|
int shadowRequestIndex = shadowRequestIndices[0];
|
|
Vector2 viewportSize = shadowResolutionRequestStorage[shadowRequestIndex].resolution;
|
|
float spotAngleForShadows = light.useCustomSpotLightShadowCone ? Math.Min(light.customSpotLightShadowCone, visibleLight.spotAngle) : visibleLight.spotAngle;
|
|
|
|
Matrix4x4 view;
|
|
Matrix4x4 deviceProjectionYFlip;
|
|
Matrix4x4 projection;
|
|
Matrix4x4 invViewProjection;
|
|
Vector4 deviceProjection;
|
|
|
|
HDShadowUtils.ExtractSpotLightData(spotAngleForShadows, light.shadowNearPlane, light.aspectRatio, light.shapeWidth,
|
|
light.shapeHeight, visibleLight, viewportSize, light.normalBias, punctualShadowFilteringQuality, usesReversedZBuffer,
|
|
out view, out invViewProjection, out projection,
|
|
out deviceProjection, out deviceProjectionYFlip,
|
|
out splitData);
|
|
|
|
hdSplit.view = view;
|
|
hdSplit.deviceProjectionMatrix = default;
|
|
hdSplit.deviceProjectionYFlip = deviceProjectionYFlip;
|
|
hdSplit.projection = projection;
|
|
hdSplit.invViewProjection = invViewProjection;
|
|
hdSplit.deviceProjection = deviceProjection;
|
|
hdSplit.cullingSphere = splitData.cullingSphere;
|
|
hdSplit.viewportSize = viewportSize;
|
|
hdSplit.forwardOffset = 0;
|
|
|
|
if (!visibleLightsAndIndicesData.HasShadowCacheUpToDate(0))
|
|
skipCulling = false;
|
|
}
|
|
|
|
outHDSplitBuffer[lightSplitBufferOffset + 0] = hdSplit;
|
|
outSplitBuffer[lightSplitBufferOffset + 0] = splitData;
|
|
|
|
uint splitExclusionMask = skipCulling ? 0b1u : 0;
|
|
|
|
outPerLightShadowCullingInfos[lightIndex] = new LightShadowCasterCullingInfo
|
|
{
|
|
splitRange = new RangeInt(lightSplitBufferOffset, 1),
|
|
projectionType = GetSpotLightCullingProjectionType(visibleLight.lightType),
|
|
splitExclusionMask = (ushort)splitExclusionMask,
|
|
};
|
|
|
|
lightSplitBufferOffset += 1;
|
|
}
|
|
|
|
return lightSplitBufferOffset - initialSplitBufferOffset;
|
|
}
|
|
|
|
int ComputePointShadowCullingSplits(UnsafeList<ShadowIndicesAndVisibleLightData> visibleLightsAndIndicesDatas, int initialSplitBufferOffset)
|
|
{
|
|
const int SplitCount = 6;
|
|
|
|
if (visibleLightsAndIndicesDatas.Length == 0)
|
|
return 0;
|
|
|
|
int lightSplitBufferOffset = initialSplitBufferOffset;
|
|
|
|
for (int i = 0; i < visibleLightsAndIndicesDatas.Length; i++)
|
|
{
|
|
ref ShadowIndicesAndVisibleLightData visibleLightsAndIndicesData = ref visibleLightsAndIndicesDatas.ElementAt(i);
|
|
ref VisibleLight visibleLight = ref visibleLightsAndIndicesData.visibleLight;
|
|
ref HDAdditionalLightDataUpdateInfo light = ref visibleLightsAndIndicesData.additionalLightUpdateInfo;
|
|
int lightIndex = visibleLightsAndIndicesData.lightIndex;
|
|
int shadowRequestIndicesBeginIndex = visibleLightsAndIndicesData.shadowRequestSetHandle.storageIndexForRequestIndices;
|
|
NativeArray<int> shadowRequestIndices = hdShadowRequestIndicesStorage.GetSubArray(shadowRequestIndicesBeginIndex, HDShadowRequest.maxLightShadowRequestsCount);
|
|
|
|
Debug.Assert(visibleLightsAndIndicesData.splitCount == SplitCount);
|
|
|
|
uint splitMaskRequest = 0;
|
|
|
|
for (int splitIndex = 0; splitIndex < SplitCount; splitIndex++)
|
|
{
|
|
ShadowSplitData splitData = default;
|
|
HDShadowCullingSplit hdSplit = default;
|
|
hdSplit.splitIndex = splitIndex;
|
|
|
|
if (visibleLightsAndIndicesData.isSplitValidMask[(uint)splitIndex])
|
|
{
|
|
int shadowRequestIndex = shadowRequestIndices[splitIndex];
|
|
Vector2 viewportSize = shadowResolutionRequestStorage[shadowRequestIndex].resolution;
|
|
|
|
Matrix4x4 view;
|
|
Matrix4x4 deviceProjectionYFlip;
|
|
Matrix4x4 projection;
|
|
Matrix4x4 invViewProjection;
|
|
Vector4 deviceProjection;
|
|
|
|
HDShadowUtils.ExtractPointLightData(cubeMapFaces, visibleLight, viewportSize, light.shadowNearPlane,
|
|
light.normalBias, (uint)splitIndex, punctualShadowFilteringQuality, usesReversedZBuffer,
|
|
out view, out invViewProjection, out projection,
|
|
out deviceProjection, out deviceProjectionYFlip,
|
|
out splitData);
|
|
|
|
hdSplit.view = view;
|
|
hdSplit.deviceProjectionMatrix = default;
|
|
hdSplit.deviceProjectionYFlip = deviceProjectionYFlip;
|
|
hdSplit.projection = projection;
|
|
hdSplit.invViewProjection = invViewProjection;
|
|
hdSplit.deviceProjection = deviceProjection;
|
|
hdSplit.cullingSphere = splitData.cullingSphere;
|
|
hdSplit.viewportSize = viewportSize;
|
|
hdSplit.forwardOffset = 0;
|
|
|
|
if (!visibleLightsAndIndicesData.HasShadowCacheUpToDate(splitIndex))
|
|
splitMaskRequest |= 1u << splitIndex;
|
|
}
|
|
|
|
outHDSplitBuffer[lightSplitBufferOffset + splitIndex] = hdSplit;
|
|
outSplitBuffer[lightSplitBufferOffset + splitIndex] = splitData;
|
|
}
|
|
|
|
uint splitExclusionMask = ~splitMaskRequest & ((1u << SplitCount) - 1);
|
|
|
|
outPerLightShadowCullingInfos[lightIndex] = new LightShadowCasterCullingInfo
|
|
{
|
|
splitRange = new RangeInt(lightSplitBufferOffset, SplitCount),
|
|
projectionType = BatchCullingProjectionType.Perspective,
|
|
splitExclusionMask = (ushort)splitExclusionMask,
|
|
};
|
|
|
|
lightSplitBufferOffset += SplitCount;
|
|
}
|
|
|
|
return lightSplitBufferOffset - initialSplitBufferOffset;
|
|
}
|
|
|
|
int ComputeAreaRectangleShadowCullingSplits(UnsafeList<ShadowIndicesAndVisibleLightData> visibleLightsAndIndicesDatas, int initialSplitBufferOffset)
|
|
{
|
|
if (visibleLightsAndIndicesDatas.Length == 0)
|
|
return 0;
|
|
|
|
int lightSplitBufferOffset = initialSplitBufferOffset;
|
|
|
|
for (int i = 0; i < visibleLightsAndIndicesDatas.Length; i++)
|
|
{
|
|
ref ShadowIndicesAndVisibleLightData visibleLightsAndIndicesData = ref visibleLightsAndIndicesDatas.ElementAt(i);
|
|
ref HDAdditionalLightDataUpdateInfo light = ref visibleLightsAndIndicesData.additionalLightUpdateInfo;
|
|
ref VisibleLight visibleLight = ref visibleLightsAndIndicesData.visibleLight;
|
|
int lightIndex = visibleLightsAndIndicesData.lightIndex;
|
|
int shadowRequestIndicesBeginIndex = visibleLightsAndIndicesData.shadowRequestSetHandle.storageIndexForRequestIndices;
|
|
NativeArray<int> shadowRequestIndices = hdShadowRequestIndicesStorage.GetSubArray(shadowRequestIndicesBeginIndex, HDShadowRequest.maxLightShadowRequestsCount);
|
|
|
|
Debug.Assert(visibleLightsAndIndicesData.splitCount == 1);
|
|
Debug.Assert(visibleLight.lightType == LightType.Rectangle);
|
|
|
|
bool skipCulling = true;
|
|
ShadowSplitData splitData = default;
|
|
HDShadowCullingSplit hdSplit = default;
|
|
hdSplit.splitIndex = 0;
|
|
|
|
if (visibleLightsAndIndicesData.isSplitValidMask[0])
|
|
{
|
|
int shadowRequestIndex = shadowRequestIndices[0];
|
|
Vector2 viewportSize = shadowResolutionRequestStorage[shadowRequestIndex].resolution;
|
|
Vector2 shapeSize = new Vector2(light.shapeWidth, light.shapeHeight);
|
|
float forwardOffset = HDAdditionalLightData.GetAreaLightOffsetForShadows(shapeSize, light.areaLightShadowCone);
|
|
|
|
Matrix4x4 view;
|
|
Matrix4x4 deviceProjectionYFlip;
|
|
Matrix4x4 projection;
|
|
Matrix4x4 invViewProjection;
|
|
Vector4 deviceProjection;
|
|
|
|
HDShadowUtils.ExtractRectangleAreaLightData(visibleLight, forwardOffset, light.areaLightShadowCone,
|
|
light.shadowNearPlane, shapeSize, viewportSize, light.normalBias, usesReversedZBuffer,
|
|
out view, out invViewProjection, out projection,
|
|
out deviceProjection, out deviceProjectionYFlip,
|
|
out splitData);
|
|
|
|
hdSplit.view = view;
|
|
hdSplit.deviceProjectionMatrix = default;
|
|
hdSplit.deviceProjectionYFlip = deviceProjectionYFlip;
|
|
hdSplit.projection = projection;
|
|
hdSplit.invViewProjection = invViewProjection;
|
|
hdSplit.deviceProjection = deviceProjection;
|
|
hdSplit.cullingSphere = splitData.cullingSphere;
|
|
hdSplit.viewportSize = viewportSize;
|
|
hdSplit.forwardOffset = forwardOffset;
|
|
|
|
if (!visibleLightsAndIndicesData.HasShadowCacheUpToDate(0))
|
|
skipCulling = false;
|
|
}
|
|
|
|
outHDSplitBuffer[lightSplitBufferOffset + 0] = hdSplit;
|
|
outSplitBuffer[lightSplitBufferOffset + 0] = splitData;
|
|
|
|
uint splitExclusionMask = skipCulling ? 0b1u : 0;
|
|
|
|
outPerLightShadowCullingInfos[lightIndex] = new LightShadowCasterCullingInfo
|
|
{
|
|
splitRange = new RangeInt(lightSplitBufferOffset, 1),
|
|
projectionType = BatchCullingProjectionType.Perspective,
|
|
splitExclusionMask = (ushort)splitExclusionMask,
|
|
};
|
|
|
|
lightSplitBufferOffset += 1;
|
|
}
|
|
|
|
return lightSplitBufferOffset - initialSplitBufferOffset;
|
|
}
|
|
|
|
[BurstDiscard]
|
|
public void ProcessDirectionalLights(CullingResults cullingResults,
|
|
UnsafeList<ShadowIndicesAndVisibleLightData> dynamicVisibleLightsAndIndicesDatas,
|
|
UnsafeList<ShadowIndicesAndVisibleLightData> cachedVisibleLightsAndIndicesDatas)
|
|
{
|
|
int splitBufferOffset = inOutSplitBufferOffset[0];
|
|
HDShadowCullingSplit* hdSplitBufferPtr = (HDShadowCullingSplit*)outHDSplitBuffer.GetUnsafePtr();
|
|
|
|
UnsafeList<HDShadowCullingSplit> dynamicDirectionalHDSplits;
|
|
UnsafeList<HDShadowCullingSplit> cachedDirectionalHDSplits;
|
|
|
|
using (computeDirectionalShadowCullingInfosMarker.Auto())
|
|
{
|
|
int dynamicSplitOffset = splitBufferOffset;
|
|
int dynamicSplitCount = ComputeDirectionalShadowCullingSplits(cullingResults, dynamicVisibleLightsAndIndicesDatas, dynamicSplitOffset);
|
|
splitBufferOffset += dynamicSplitCount;
|
|
|
|
int cachedSplitOffset = splitBufferOffset;
|
|
int cachedSplitCount = ComputeDirectionalShadowCullingSplits(cullingResults, cachedVisibleLightsAndIndicesDatas, cachedSplitOffset);
|
|
splitBufferOffset += cachedSplitCount;
|
|
|
|
dynamicDirectionalHDSplits = new UnsafeList<HDShadowCullingSplit>(hdSplitBufferPtr + dynamicSplitOffset, dynamicSplitCount);
|
|
cachedDirectionalHDSplits = new UnsafeList<HDShadowCullingSplit>(hdSplitBufferPtr + cachedSplitOffset, cachedSplitCount);
|
|
}
|
|
|
|
inOutSplitBufferOffset[0] = splitBufferOffset;
|
|
*outDynamicDirectionalHDSplits = dynamicDirectionalHDSplits;
|
|
*outCachedDirectionalHDSplits = cachedDirectionalHDSplits;
|
|
}
|
|
|
|
[BurstDiscard]
|
|
int ComputeDirectionalShadowCullingSplits(CullingResults cullingResults, UnsafeList<ShadowIndicesAndVisibleLightData> visibleLightsAndIndicesDatas, int initialSplitBufferOffset)
|
|
{
|
|
if (visibleLightsAndIndicesDatas.Length == 0)
|
|
return 0;
|
|
|
|
int lightSplitBufferOffset = initialSplitBufferOffset;
|
|
|
|
for (int i = 0; i < visibleLightsAndIndicesDatas.Length; i++)
|
|
{
|
|
ref ShadowIndicesAndVisibleLightData visibleLightsAndIndicesData = ref visibleLightsAndIndicesDatas.ElementAt(i);
|
|
ref HDAdditionalLightDataUpdateInfo light = ref visibleLightsAndIndicesData.additionalLightUpdateInfo;
|
|
int splitCount = visibleLightsAndIndicesData.splitCount;
|
|
int lightIndex = visibleLightsAndIndicesData.lightIndex;
|
|
int shadowRequestIndicesBeginIndex = visibleLightsAndIndicesData.shadowRequestSetHandle.storageIndexForRequestIndices;
|
|
NativeArray<int> shadowRequestIndices = hdShadowRequestIndicesStorage.GetSubArray(shadowRequestIndicesBeginIndex, splitCount);
|
|
|
|
uint splitMaskRequest = 0;
|
|
|
|
for (int splitIndex = 0; splitIndex < splitCount; splitIndex++)
|
|
{
|
|
ShadowSplitData splitData = default;
|
|
HDShadowCullingSplit hdSplit = default;
|
|
hdSplit.splitIndex = splitIndex;
|
|
|
|
if (visibleLightsAndIndicesData.isSplitValidMask[(uint)splitIndex])
|
|
{
|
|
int shadowRequestIndex = shadowRequestIndices[splitIndex];
|
|
Vector2 viewportSize = shadowResolutionRequestStorage[shadowRequestIndex].resolution;
|
|
|
|
Matrix4x4 view;
|
|
Matrix4x4 deviceProjectionYFlip;
|
|
Matrix4x4 deviceProjectionMatrix;
|
|
Matrix4x4 projection;
|
|
Matrix4x4 invViewProjection;
|
|
Vector4 deviceProjection;
|
|
|
|
HDShadowUtils.ExtractDirectionalLightData(viewportSize, (uint)splitIndex, cascadeShadowSplitCount,
|
|
cascadeShadowSplits, shadowNearPlaneOffset, cullingResults, lightIndex,
|
|
out view, out invViewProjection, out projection,
|
|
out deviceProjectionMatrix, out deviceProjection, out deviceProjectionYFlip, out splitData);
|
|
|
|
hdSplit.view = view;
|
|
hdSplit.deviceProjectionMatrix = deviceProjectionMatrix;
|
|
hdSplit.deviceProjectionYFlip = deviceProjectionYFlip;
|
|
hdSplit.projection = projection;
|
|
hdSplit.invViewProjection = invViewProjection;
|
|
hdSplit.deviceProjection = deviceProjection;
|
|
hdSplit.cullingSphere = splitData.cullingSphere;
|
|
hdSplit.viewportSize = viewportSize;
|
|
hdSplit.forwardOffset = 0;
|
|
|
|
if (!visibleLightsAndIndicesData.HasShadowCacheUpToDate(splitIndex))
|
|
splitMaskRequest |= 1u << splitIndex;
|
|
}
|
|
|
|
outHDSplitBuffer[lightSplitBufferOffset + splitIndex] = hdSplit;
|
|
outSplitBuffer[lightSplitBufferOffset + splitIndex] = splitData;
|
|
}
|
|
|
|
uint splitExclusionMask = ~splitMaskRequest & ((1u << splitCount) - 1);
|
|
|
|
outPerLightShadowCullingInfos[lightIndex] = new LightShadowCasterCullingInfo
|
|
{
|
|
splitRange = new RangeInt(lightSplitBufferOffset, splitCount),
|
|
projectionType = BatchCullingProjectionType.Orthographic,
|
|
splitExclusionMask = (ushort)splitExclusionMask,
|
|
};
|
|
|
|
lightSplitBufferOffset += splitCount;
|
|
}
|
|
|
|
return lightSplitBufferOffset - initialSplitBufferOffset;
|
|
}
|
|
|
|
BitArray8 ComputeNeedCacheUpdateMask(ref HDAdditionalLightDataUpdateInfo lightUpdateInfo,
|
|
ref VisibleLight visibleLight,
|
|
int splitCount,
|
|
ref float3 newCachedDirectionalAngles)
|
|
{
|
|
BitArray8 needCacheUpdateMask = new BitArray8(0);
|
|
|
|
if (lightUpdateInfo.hasCachedComponent)
|
|
{
|
|
float3 lightEulerAngles = visibleLight.localToWorldMatrix.rotation.eulerAngles;
|
|
int lightIdxForCachedShadows = lightUpdateInfo.lightIdxForCachedShadows;
|
|
bool shadowHasAtlasPlacement = lightIdxForCachedShadows != -1;
|
|
|
|
bool isDirectional = visibleLight.lightType == LightType.Directional;
|
|
bool isSpot = visibleLight.lightType == LightType.Spot || visibleLight.lightType == LightType.Pyramid || visibleLight.lightType == LightType.Box;
|
|
bool isPoint = visibleLight.lightType == LightType.Point;
|
|
bool isArea = visibleLight.lightType == LightType.Rectangle;
|
|
|
|
if (isDirectional)
|
|
{
|
|
bool needsRenderingDueToTransformChange = false;
|
|
if (lightUpdateInfo.updateUponLightMovement)
|
|
{
|
|
float angleDiffThreshold = lightUpdateInfo.cachedShadowAngleUpdateThreshold;
|
|
float3 angleDiff = newCachedDirectionalAngles - lightEulerAngles;
|
|
// Any angle difference
|
|
if (math.abs(angleDiff.x) > angleDiffThreshold || math.abs(angleDiff.y) > angleDiffThreshold || math.abs(angleDiff.z) > angleDiffThreshold)
|
|
{
|
|
newCachedDirectionalAngles = lightEulerAngles;
|
|
needsRenderingDueToTransformChange = true;
|
|
}
|
|
}
|
|
|
|
BitArray8 directionalShadowPendingUpdate = shadowManager.cachedShadowManager.directionalShadowPendingUpdate;
|
|
|
|
for (int i = 0; i < splitCount; i++)
|
|
{
|
|
bool directionalShadowIdxPendingUpdate = directionalShadowPendingUpdate[(uint)(lightIdxForCachedShadows + i)];
|
|
bool needToUpdateCachedContent = shadowHasAtlasPlacement && (needsRenderingDueToTransformChange || directionalShadowIdxPendingUpdate);
|
|
|
|
needCacheUpdateMask[(uint)i] = needToUpdateCachedContent;
|
|
}
|
|
}
|
|
else if (isSpot || isPoint || isArea)
|
|
{
|
|
HDCachedShadowAtlasDataForShadowRequestUpdateJob cachedShadowAtlas = default;
|
|
if (isSpot || isPoint)
|
|
{
|
|
cachedShadowAtlas = shadowManager.cachedShadowManager.punctualShadowAtlas;
|
|
}
|
|
else if (isArea)
|
|
{
|
|
cachedShadowAtlas = shadowManager.cachedShadowManager.areaShadowAtlas;
|
|
}
|
|
|
|
bool needsRenderingDueToTransformChange = false;
|
|
if (lightUpdateInfo.updateUponLightMovement)
|
|
{
|
|
if (cachedShadowAtlas.transformCaches.TryGetValue(lightUpdateInfo.lightIdxForCachedShadows, out HDCachedShadowAtlas.CachedTransform cachedTransform))
|
|
{
|
|
float positionThreshold = lightUpdateInfo.cachedShadowTranslationUpdateThreshold;
|
|
float3 positionDiffVec = cachedTransform.position - visibleLight.GetPosition();
|
|
float positionDiff = math.dot(positionDiffVec, positionDiffVec);
|
|
if (positionDiff > positionThreshold * positionThreshold)
|
|
needsRenderingDueToTransformChange = true;
|
|
|
|
float angleDiffThreshold = lightUpdateInfo.cachedShadowAngleUpdateThreshold;
|
|
float3 cachedAngles = cachedTransform.angles;
|
|
float3 angleDiff = cachedAngles - lightEulerAngles;
|
|
// Any angle difference
|
|
if (math.abs(angleDiff.x) > angleDiffThreshold || math.abs(angleDiff.y) > angleDiffThreshold || math.abs(angleDiff.z) > angleDiffThreshold)
|
|
{
|
|
needsRenderingDueToTransformChange = true;
|
|
}
|
|
|
|
if (needsRenderingDueToTransformChange)
|
|
{
|
|
// Update the record
|
|
cachedTransform.position = visibleLight.GetPosition();
|
|
cachedTransform.angles = lightEulerAngles;
|
|
cachedShadowAtlas.transformCaches[lightUpdateInfo.lightIdxForCachedShadows] = cachedTransform;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < splitCount; i++)
|
|
{
|
|
bool needToUpdateCachedContent = false;
|
|
|
|
if (shadowHasAtlasPlacement)
|
|
{
|
|
int cachedShadowID = lightUpdateInfo.lightIdxForCachedShadows + i;
|
|
|
|
bool shadowsPendingRenderingContainedShadowID = cachedShadowAtlas.shadowsPendingRendering.Remove(cachedShadowID);
|
|
needToUpdateCachedContent = needsRenderingDueToTransformChange || shadowsPendingRenderingContainedShadowID;
|
|
|
|
if (shadowsPendingRenderingContainedShadowID)
|
|
{
|
|
// Handshake with the cached shadow manager to notify about the rendering.
|
|
// Technically the rendering has not happened yet, but it is scheduled.
|
|
cachedShadowAtlas.shadowsWithValidData.TryAdd(cachedShadowID, cachedShadowID);
|
|
}
|
|
}
|
|
|
|
needCacheUpdateMask[(uint)i] = needToUpdateCachedContent;
|
|
}
|
|
}
|
|
}
|
|
|
|
return needCacheUpdateMask;
|
|
}
|
|
}
|
|
|
|
static BatchCullingProjectionType GetSpotLightCullingProjectionType(LightType lightType)
|
|
{
|
|
if (lightType == LightType.Box)
|
|
{
|
|
return BatchCullingProjectionType.Orthographic;
|
|
}
|
|
else if (lightType == LightType.Spot || lightType == LightType.Pyramid)
|
|
{
|
|
return BatchCullingProjectionType.Perspective;
|
|
}
|
|
|
|
return BatchCullingProjectionType.Unknown;
|
|
}
|
|
|
|
static Vector3 GetCascadeRatiosAsVector3(float[] cascadeRatios)
|
|
{
|
|
Vector3 vec3 = Vector3.zero;
|
|
for (int i = 0; i < math.min(cascadeRatios.Length, 3); i++)
|
|
{
|
|
vec3[i] = cascadeRatios[i];
|
|
}
|
|
return vec3;
|
|
}
|
|
}
|
|
}
|