Files
2026-11-01 23:39:55 +02:00

220 lines
8.8 KiB
C#

using Unity.Mathematics;
using System.Collections.Generic;
namespace UnityEngine.Rendering.Universal
{
internal class LayerBatch
{
#if UNITY_EDITOR
internal int startIndex;
internal int endIndex;
#endif
internal int startLayerID;
internal int endLayerValue;
internal SortingLayerRange layerRange;
internal LightStats lightStats;
internal bool useNormals;
internal List<Light2D> lights = new List<Light2D>();
internal List<int> shadowIndices = new List<int>();
internal List<ShadowCasterGroup2D> shadowCasters = new List<ShadowCasterGroup2D>();
internal int[] activeBlendStylesIndices;
// This range check uses SortingLayer.value.
internal bool IsValueWithinLayerRange(int value)
{
return value >= layerRange.lowerBound && value <= layerRange.upperBound;
}
}
internal static class LayerUtility
{
private static LayerBatch[] s_LayerBatches;
#if UNITY_EDITOR
[RuntimeInitializeOnLoadMethod]
static void ResetStaticsOnLoad()
{
s_LayerBatches = null;
}
#endif
private static bool CanBatchLightsInLayer(int layerIndex1, int layerIndex2, SortingLayer[] sortingLayers, ILight2DCullResult lightCullResult)
{
var layerId1 = sortingLayers[layerIndex1].id;
var layerId2 = sortingLayers[layerIndex2].id;
foreach (var light in lightCullResult.visibleLights)
{
// If the lit layers are different don't batch.
if (light.IsLitLayer(layerId1) != light.IsLitLayer(layerId2))
return false;
}
foreach (var group in lightCullResult.visibleShadows)
{
foreach (var shadowCaster in group.GetShadowCasters())
{
// Don't batch when the layer has different shadow casters
if (shadowCaster.IsShadowedLayer(layerId1) != shadowCaster.IsShadowedLayer(layerId2))
return false;
}
}
return true;
}
private static bool CanBatchCameraSortingLayer(int startLayerIndex, SortingLayer[] sortingLayers, Renderer2DData rendererData)
{
if (rendererData.useCameraSortingLayerTexture)
{
var cameraSortingLayerBoundsIndex = rendererData.GetCameraSortingLayerBoundsIndex();
return sortingLayers[startLayerIndex].value == cameraSortingLayerBoundsIndex;
}
return false;
}
private static int FindUpperBoundInBatch(int startLayerIndex, SortingLayer[] sortingLayers, Renderer2DData rendererData)
{
// break layer if camera sorting layer is active
if (CanBatchCameraSortingLayer(startLayerIndex, sortingLayers, rendererData))
return startLayerIndex;
// start checking at the next layer
for (var i = startLayerIndex + 1; i < sortingLayers.Length; i++)
{
if (!CanBatchLightsInLayer(startLayerIndex, i, sortingLayers, rendererData.lightCullResult))
return i - 1;
if (CanBatchCameraSortingLayer(i, sortingLayers, rendererData))
return i;
}
return sortingLayers.Length - 1;
}
private static void InitializeBatchInfos(SortingLayer[] cachedSortingLayers)
{
var count = cachedSortingLayers.Length;
var needInit = s_LayerBatches == null;
if (s_LayerBatches is null)
{
s_LayerBatches = new LayerBatch[count];
}
#if UNITY_EDITOR
// we should fix. Make a non allocating version of this
if (!Application.isPlaying && s_LayerBatches.Length != count)
{
s_LayerBatches = new LayerBatch[count];
needInit = true;
}
#endif
if (needInit)
{
for (var i = 0; i < s_LayerBatches.Length; i++)
s_LayerBatches[i] = new LayerBatch();
}
}
public static LayerBatch[] CalculateBatches(Renderer2DData rendererData, out int batchCount)
{
var cachedSortingLayers = Light2DManager.GetCachedSortingLayer();
InitializeBatchInfos(cachedSortingLayers);
bool anyNormals = false;
batchCount = 0;
for (var i = 0; i < cachedSortingLayers.Length;)
{
var layerToRender = cachedSortingLayers[i].id;
var layerBatch = s_LayerBatches[batchCount++];
var lightStats = rendererData.lightCullResult.GetLightStatsByLayer(layerToRender, ref layerBatch);
// Find the highest layer that share the same set of lights and shadows as this layer.
var upperLayerInBatch = FindUpperBoundInBatch(i, cachedSortingLayers, rendererData);
// Some renderers override their sorting layer value with short.MinValue or short.MaxValue.
// When drawing the first sorting layer, we should include the range from short.MinValue to layerValue.
// Similarly, when drawing the last sorting layer, include the range from layerValue to short.MaxValue.
var startLayerValue = (short)cachedSortingLayers[i].value;
var lowerBound = (i == 0) ? short.MinValue : startLayerValue;
var endLayerValue = (short)cachedSortingLayers[upperLayerInBatch].value;
var upperBound = (upperLayerInBatch == cachedSortingLayers.Length - 1) ? short.MaxValue : endLayerValue;
// Renderer within this range share the same set of lights so they should be rendered together.
var sortingLayerRange = new SortingLayerRange(lowerBound, upperBound);
#if UNITY_EDITOR
layerBatch.startIndex = i;
layerBatch.endIndex = upperLayerInBatch;
#endif
layerBatch.startLayerID = layerToRender;
layerBatch.endLayerValue = endLayerValue;
layerBatch.layerRange = sortingLayerRange;
layerBatch.lightStats = lightStats;
anyNormals |= layerBatch.lightStats.useNormalMap;
i = upperLayerInBatch + 1;
}
// Account for Sprite Mask and normal map usage as there might be masks on a different layer that need to mask out the normals
for (var i = 0; i < batchCount; ++i)
{
var layerBatch = s_LayerBatches[i];
var hasSpriteMask = SpriteMaskUtility.HasSpriteMaskInLayerRange(layerBatch.layerRange);
layerBatch.useNormals = layerBatch.lightStats.useNormalMap || (anyNormals && hasSpriteMask);
}
SetupActiveBlendStyles();
return s_LayerBatches;
}
public static void GetFilterSettings(Renderer2DData rendererData, LayerBatch layerBatch, out FilteringSettings filterSettings)
{
filterSettings = FilteringSettings.defaultValue;
filterSettings.renderQueueRange = RenderQueueRange.all;
filterSettings.layerMask = rendererData.layerMask;
filterSettings.renderingLayerMask = 0xFFFFFFFF;
filterSettings.sortingLayerRange = layerBatch.layerRange;
}
static void SetupActiveBlendStyles()
{
// Calculate active blend styles to save on total light textures allocated
for (int i = 0; i < s_LayerBatches.Length; ++i)
{
ref var layer = ref s_LayerBatches[i];
// Determine number of blend styles used
int size = 0;
for (var blendStyleIndex = 0; blendStyleIndex < RendererLighting.k_ShapeLightTextureIDs.Length; blendStyleIndex++)
{
var blendStyleMask = (uint)(1 << blendStyleIndex);
var blendStyleUsed = (layer.lightStats.blendStylesUsed & blendStyleMask) > 0;
if (blendStyleUsed)
size++;
}
if (layer.activeBlendStylesIndices == null || layer.activeBlendStylesIndices.Length != size)
layer.activeBlendStylesIndices = new int[size];
// Save indices so we don't have to copy the whole Light2DBlendStyle struct
var index = 0;
for (var blendStyleIndex = 0; blendStyleIndex < RendererLighting.k_ShapeLightTextureIDs.Length; blendStyleIndex++)
{
var blendStyleMask = (uint)(1 << blendStyleIndex);
var blendStyleUsed = (layer.lightStats.blendStylesUsed & blendStyleMask) > 0;
if (blendStyleUsed)
layer.activeBlendStylesIndices[index++] = blendStyleIndex;
}
}
}
}
}