UnityGame/Library/PackageCache/com.unity.render-pipelines.universal/Runtime/2D/Passes/Utility/LightBatch.cs

247 lines
8.3 KiB
C#
Raw Permalink Normal View History

2024-10-27 10:53:47 +03:00
using UnityEngine.Experimental.Rendering;
using Unity.Collections;
using Unity.Mathematics;
using Unity.Collections.LowLevel.Unsafe;
namespace UnityEngine.Rendering.Universal
{
internal class LightBuffer
{
static readonly internal int kMax = 2048 * 8;
static readonly internal int kCount = 1;
static readonly internal int kLightMod = 64;
static readonly internal int kBatchMax = 256;
private GraphicsBuffer m_GraphicsBuffer;
private NativeArray<int> m_Markers = new NativeArray<int>(kBatchMax, Allocator.Persistent, NativeArrayOptions.ClearMemory);
private NativeArray<PerLight2D> m_NativeBuffer = new NativeArray<PerLight2D>(kMax, Allocator.Persistent, NativeArrayOptions.ClearMemory);
internal GraphicsBuffer graphicsBuffer
{
get
{
if (null == m_GraphicsBuffer)
m_GraphicsBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, kMax, UnsafeUtility.SizeOf<PerLight2D>());
return m_GraphicsBuffer;
}
}
internal NativeArray<int> lightMarkers
{
get
{
return m_Markers;
}
}
internal NativeArray<PerLight2D> nativeBuffer
{
get
{
return m_NativeBuffer;
}
}
internal void Release()
{
m_GraphicsBuffer.Release();
m_GraphicsBuffer = null;
}
internal void Reset()
{
unsafe { UnsafeUtility.MemClear(m_Markers.GetUnsafePtr(), UnsafeUtility.SizeOf<int>() * LightBuffer.kBatchMax); }
unsafe { UnsafeUtility.MemClear(m_NativeBuffer.GetUnsafePtr(), UnsafeUtility.SizeOf<PerLight2D>() * LightBuffer.kMax); }
}
}
// The idea is to avoid CPU cost when rendering meshes with the same shader (Consider this a light-weight SRP batcher). To identify the Mesh instance ID in the Light Buffer we utilize a Slot Index
// identified from the Blue Channel of the Vertex Colors (Solely used for this purpose). This can batch a maximum of kLightMod meshes in best-case scenario. Simple but no optizations have been added yet
internal class LightBatch
{
static readonly ProfilingSampler profilingDrawBatched = new ProfilingSampler("Light2D Batcher");
static readonly int k_BufferOffset = Shader.PropertyToID("_BatchBufferOffset");
static int sBatchIndexCounter = 0; // For LightMesh asset conditioning to facilitate batching.
private static int batchLightMod => LightBuffer.kLightMod;
private static float batchRunningIndex => (sBatchIndexCounter++) % LightBuffer.kLightMod / (float)LightBuffer.kLightMod;
// Should be in Sync with USE_STRUCTURED_BUFFER_FOR_LIGHT2D_DATA
public static bool isBatchingSupported => false;
private int[] subsets = new int[LightBuffer.kMax];
private Mesh[] lightMeshes = new Mesh[LightBuffer.kMax];
private Matrix4x4[] matrices = new Matrix4x4[LightBuffer.kMax];
private LightBuffer[] lightBuffer = new LightBuffer[LightBuffer.kCount];
private Light2D cachedLight;
private Material cachedMaterial;
private int hashCode = 0;
private int lightCount = 0;
private int maxIndex = 0;
private int batchCount = 0;
private int activeCount = 0;
internal NativeArray<PerLight2D> nativeBuffer
{
get
{
if (null == lightBuffer[activeCount])
lightBuffer[activeCount] = new LightBuffer();
return lightBuffer[activeCount].nativeBuffer;
}
}
internal GraphicsBuffer graphicsBuffer
{
get
{
if (null == lightBuffer[activeCount])
lightBuffer[activeCount] = new LightBuffer();
return lightBuffer[activeCount].graphicsBuffer;
}
}
internal NativeArray<int> lightMarker
{
get
{
if (null == lightBuffer[activeCount])
lightBuffer[activeCount] = new LightBuffer();
return lightBuffer[activeCount].lightMarkers;
}
}
internal PerLight2D GetLight(int index) => nativeBuffer[index];
internal static int batchSlotIndex => (int)(batchRunningIndex * LightBuffer.kLightMod);
#if UNITY_EDITOR
static bool kRegisterCallback = false;
#endif
internal void SetLight(int index, PerLight2D light)
{
var buffer = nativeBuffer;
buffer[index] = light;
}
internal static float GetBatchColor()
{
return (float)batchSlotIndex / (float)batchLightMod;
}
internal static int GetBatchSlotIndex(float channelColor)
{
return (int)(channelColor * LightBuffer.kLightMod);
}
static int Hash(Light2D light, Material material)
{
unchecked
{
int _hashCode = (int)2166136261;
_hashCode = _hashCode * 16777619 ^ material.GetHashCode();
_hashCode = _hashCode * 16777619 ^ (light.lightCookieSprite == null ? 0 : light.lightCookieSprite.GetHashCode());
return _hashCode;
}
}
void Validate()
{
#if UNITY_EDITOR
if (!kRegisterCallback)
UnityEditor.AssemblyReloadEvents.beforeAssemblyReload += OnAssemblyReload;
kRegisterCallback = true;
#endif
}
void OnAssemblyReload()
{
for (int i = 0; i < LightBuffer.kCount; ++i)
lightBuffer[activeCount].Release();
}
void ResetInternals()
{
for (int i = 0; i < LightBuffer.kCount; ++i)
if (null != lightBuffer[i])
lightBuffer[i].Reset();
}
void SetBuffer()
{
Validate();
graphicsBuffer.SetData(nativeBuffer, lightCount, lightCount, math.min(LightBuffer.kBatchMax, LightBuffer.kMax - lightCount));
}
internal int SlotIndex(int x)
{
return lightCount + x;
}
internal void Reset()
{
if (isBatchingSupported)
{
maxIndex = 0;
hashCode = 0;
batchCount = 0;
lightCount = 0;
activeCount = 0;
Shader.SetGlobalBuffer("_Light2DBuffer", graphicsBuffer);
}
}
internal bool CanBatch(Light2D light, Material material, int index, out int lightHash)
{
lightHash = Hash(light, material);
hashCode = (hashCode == 0) ? lightHash : hashCode;
if (batchCount == 0)
{
hashCode = lightHash;
}
else if (hashCode != lightHash || SlotIndex(index) >= LightBuffer.kMax || lightMarker[index] == 1)
{
hashCode = lightHash;
return false;
}
return true;
}
internal bool AddBatch(Light2D light, Material material, Matrix4x4 mat, Mesh mesh, int subset, int lightHash, int index)
{
Debug.Assert(lightHash == hashCode);
cachedLight = light;
cachedMaterial = material;
matrices[batchCount] = mat;
lightMeshes[batchCount] = mesh;
subsets[batchCount] = subset;
batchCount++;
maxIndex = math.max(maxIndex, index);
var lightMark = lightMarker;
lightMark[index] = 1;
return true;
}
internal void Flush(RasterCommandBuffer cmd)
{
if (batchCount > 0)
{
using (new ProfilingScope(cmd, profilingDrawBatched))
{
SetBuffer();
cmd.SetGlobalInt(k_BufferOffset, lightCount);
cmd.DrawMultipleMeshes(matrices, lightMeshes, subsets, batchCount, cachedMaterial, -1, null);
}
lightCount = lightCount + maxIndex + 1;
}
for (int i = 0; i < batchCount; ++i)
lightMeshes[i] = null;
ResetInternals();
batchCount = 0;
maxIndex = 0;
}
}
}