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

358 lines
14 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using Unity.Collections.LowLevel.Unsafe;
using UnityEngine.Assertions;
using UnityEngine.Experimental.Rendering;
namespace UnityEngine.Rendering.HighDefinition
{
internal static class HDProbeSystem
{
static HDProbeSystemInternal s_Instance;
static HDProbeSystem()
{
s_Instance = new HDProbeSystemInternal();
#if UNITY_EDITOR
UnityEditor.AssemblyReloadEvents.beforeAssemblyReload += DisposeStaticInstance;
UnityEditor.EditorApplication.quitting += DisposeStaticInstance;
#else
Application.quitting += DisposeStaticInstance;
#endif
}
// Don't set the reference to null
// Only dispose resources
static void DisposeStaticInstance() => s_Instance.Dispose();
public static ReflectionSystemParameters Parameters
{
get => s_Instance.Parameters;
set => s_Instance.Parameters = value;
}
public static IEnumerable<HDProbe> realtimeViewDependentProbes => s_Instance.realtimeViewDependentProbes;
public static IEnumerable<HDProbe> realtimeViewIndependentProbes => s_Instance.realtimeViewIndependentProbes;
public static IEnumerable<HDProbe> bakedProbes => s_Instance.bakedProbes;
public static int bakedProbeCount => s_Instance.bakedProbeCount;
public static void RegisterProbe(HDProbe probe) => s_Instance.RegisterProbe(probe);
public static void UnregisterProbe(HDProbe probe) => s_Instance.UnregisterProbe(probe);
public static void Render(
HDProbe probe, Transform viewerTransform,
Texture outTarget, out HDProbe.RenderData outRenderData,
bool forceFlipY = false,
float referenceFieldOfView = 90,
float referenceAspect = 1
)
{
var positionSettings = ProbeCapturePositionSettings.ComputeFrom(probe, viewerTransform);
HDRenderUtilities.Render(
probe.settings,
positionSettings,
outTarget,
out var cameraSettings, out var cameraPosition,
forceFlipY,
referenceFieldOfView: referenceFieldOfView,
referenceAspect: referenceAspect
);
outRenderData = new HDProbe.RenderData(cameraSettings, cameraPosition);
}
public static void AssignRenderData(
HDProbe probe,
HDProbe.RenderData renderData,
ProbeSettings.Mode targetMode
)
{
switch (targetMode)
{
case ProbeSettings.Mode.Baked: probe.bakedRenderData = renderData; break;
case ProbeSettings.Mode.Custom: probe.customRenderData = renderData; break;
case ProbeSettings.Mode.Realtime: probe.realtimeRenderData = renderData; break;
}
}
public static HDProbeCullState PrepareCull(Camera camera)
=> s_Instance.PrepareCull(camera);
public static void QueryCullResults(HDProbeCullState state, ref HDProbeCullingResults results)
=> s_Instance.QueryCullResults(state, ref results);
public static Texture CreateRenderTargetForMode(HDProbe probe, ProbeSettings.Mode targetMode)
{
Texture target = null;
var hd = (HDRenderPipeline)RenderPipelineManager.currentPipeline;
var settings = probe.settings;
switch (targetMode)
{
case ProbeSettings.Mode.Realtime:
{
var format = (GraphicsFormat)hd.currentPlatformRenderPipelineSettings.lightLoopSettings.reflectionProbeFormat;
switch (settings.type)
{
case ProbeSettings.ProbeType.PlanarProbe:
target = HDRenderUtilities.CreatePlanarProbeRenderTarget(
(int)probe.resolution, format
);
break;
case ProbeSettings.ProbeType.ReflectionProbe:
target = HDRenderUtilities.CreateReflectionProbeRenderTarget(
(int)probe.cubeResolution, format
);
break;
}
break;
}
case ProbeSettings.Mode.Baked:
case ProbeSettings.Mode.Custom:
{
// Custom and Baked texture only support float16 for now
var format = GraphicsFormat.R16G16B16A16_SFloat;
switch (settings.type)
{
case ProbeSettings.ProbeType.PlanarProbe:
target = HDRenderUtilities.CreatePlanarProbeRenderTarget(
(int)probe.resolution, format
);
break;
case ProbeSettings.ProbeType.ReflectionProbe:
target = HDRenderUtilities.CreateReflectionProbeRenderTarget(
(int)probe.cubeResolution, format
);
break;
}
break;
}
}
return target;
}
static Texture CreateAndSetRenderTargetIfRequired(HDProbe probe, ProbeSettings.Mode targetMode)
{
var settings = probe.settings;
Texture target = probe.GetTexture(targetMode);
if (target != null)
return target;
target = CreateRenderTargetForMode(probe, targetMode);
probe.SetTexture(targetMode, target);
return target;
}
}
class HDProbeSystemInternal : IDisposable
{
HashSet<HDProbe> m_BakedProbes = new HashSet<HDProbe>();
HashSet<HDProbe> m_RealtimeViewDependentProbes = new HashSet<HDProbe>();
HashSet<HDProbe> m_RealtimeViewIndependentProbes = new HashSet<HDProbe>();
int m_PlanarProbeCount = 0;
bool m_RebuildPlanarProbeArray;
HashSet<HDProbe> m_PlanarProbes = new HashSet<HDProbe>();
PlanarReflectionProbe[] m_PlanarProbesArray = new PlanarReflectionProbe[32];
BoundingSphere[] m_PlanarProbeBounds = new BoundingSphere[32];
CullingGroup m_PlanarProbeCullingGroup = new CullingGroup();
public IEnumerable<HDProbe> bakedProbes
{ get { RemoveDestroyedProbes(m_BakedProbes); return m_BakedProbes; } }
public IEnumerable<HDProbe> realtimeViewDependentProbes
{ get { RemoveDestroyedProbes(m_RealtimeViewDependentProbes); return m_RealtimeViewDependentProbes; } }
public IEnumerable<HDProbe> realtimeViewIndependentProbes
{ get { RemoveDestroyedProbes(m_RealtimeViewIndependentProbes); return m_RealtimeViewIndependentProbes; } }
public int bakedProbeCount => m_BakedProbes.Count;
public ReflectionSystemParameters Parameters;
public void Dispose()
{
m_PlanarProbeCullingGroup.Dispose();
m_PlanarProbeCullingGroup = null;
}
internal void RegisterProbe(HDProbe probe)
{
var settings = probe.settings;
switch (settings.mode)
{
case ProbeSettings.Mode.Baked:
m_BakedProbes.Add(probe);
break;
case ProbeSettings.Mode.Realtime:
switch (settings.type)
{
case ProbeSettings.ProbeType.PlanarProbe:
if (!m_RealtimeViewDependentProbes.Contains(probe))
m_RealtimeViewDependentProbes.Add(probe);
break;
case ProbeSettings.ProbeType.ReflectionProbe:
if (!m_RealtimeViewIndependentProbes.Contains(probe))
m_RealtimeViewIndependentProbes.Add(probe);
break;
}
break;
}
switch (settings.type)
{
case ProbeSettings.ProbeType.PlanarProbe:
{
if (m_PlanarProbes.Add((PlanarReflectionProbe)probe))
{
// Insert in the array
// Grow the arrays
if (m_PlanarProbeCount >= m_PlanarProbesArray.Length)
{
Array.Resize(ref m_PlanarProbesArray, m_PlanarProbeCount * 2);
Array.Resize(ref m_PlanarProbeBounds, m_PlanarProbeCount * 2);
}
m_PlanarProbesArray[m_PlanarProbeCount] = (PlanarReflectionProbe)probe;
m_PlanarProbeBounds[m_PlanarProbeCount] = ((PlanarReflectionProbe)probe).boundingSphere;
++m_PlanarProbeCount;
}
break;
}
}
}
internal void UnregisterProbe(HDProbe probe)
{
m_BakedProbes.Remove(probe);
m_RealtimeViewDependentProbes.Remove(probe);
m_RealtimeViewIndependentProbes.Remove(probe);
// Remove swap back
if (m_PlanarProbes.Remove(probe))
{
// It is best to rebuild the full array when we need it instead of doing it at each unregister.
// So we mark it as dirty.
m_RebuildPlanarProbeArray = true;
}
}
internal HDProbeCullState PrepareCull(Camera camera)
{
// Can happens right before a domain reload
// The CullingGroup is disposed at that point
if (m_PlanarProbeCullingGroup == null)
return default;
RebuildPlanarProbeArrayIfRequired();
UpdateBoundsAndRemoveDestroyedProbes(m_PlanarProbesArray, m_PlanarProbeBounds, ref m_PlanarProbeCount);
m_PlanarProbeCullingGroup.targetCamera = camera;
m_PlanarProbeCullingGroup.SetBoundingSpheres(m_PlanarProbeBounds);
m_PlanarProbeCullingGroup.SetBoundingSphereCount(m_PlanarProbeCount);
var stateHash = ComputeStateHashDebug(m_PlanarProbeBounds, m_PlanarProbesArray, m_PlanarProbeCount);
return new HDProbeCullState(m_PlanarProbeCullingGroup, m_PlanarProbesArray, stateHash);
}
void RebuildPlanarProbeArrayIfRequired()
{
if (m_RebuildPlanarProbeArray)
{
RemoveDestroyedProbes(m_PlanarProbes);
m_RebuildPlanarProbeArray = false;
var i = 0;
foreach (var probe in m_PlanarProbes)
{
m_PlanarProbesArray[i] = (PlanarReflectionProbe)probe;
++i;
}
m_PlanarProbeCount = m_PlanarProbes.Count;
}
}
int[] m_QueryCullResults_Indices;
internal void QueryCullResults(HDProbeCullState state, ref HDProbeCullingResults results)
{
Assert.IsNotNull(state.cullingGroup, "Culling was not prepared, please prepare cull before performing it.");
Assert.IsNotNull(state.hdProbes, "Culling was not prepared, please prepare cull before performing it.");
var stateHash = ComputeStateHashDebug(m_PlanarProbeBounds, m_PlanarProbesArray, m_PlanarProbeCount);
Assert.AreEqual(stateHash, state.stateHash, "HDProbes changes since culling was prepared, this will lead to incorrect results.");
results.Reset();
Array.Resize(
ref m_QueryCullResults_Indices,
Parameters.maxActivePlanarReflectionProbe + Parameters.maxActiveEnvReflectionProbe
);
var indexCount = state.cullingGroup.QueryIndices(true, m_QueryCullResults_Indices, 0);
for (var i = 0; i < indexCount; ++i)
results.AddProbe(state.hdProbes[m_QueryCullResults_Indices[i]]);
}
static void RemoveDestroyedProbes(HashSet<HDProbe> probes)
=> probes.RemoveWhere(p => p == null || p.Equals(null));
static void UpdateBoundsAndRemoveDestroyedProbes(PlanarReflectionProbe[] probes, BoundingSphere[] bounds, ref int count)
{
for (int i = 0; i < count; ++i)
{
if (probes[i] == null || probes[i].Equals(null))
{
probes[i] = probes[count - 1];
bounds[i] = bounds[count - 1];
probes[count - 1] = null;
--count;
}
if (probes[i])
{
bounds[i] = probes[i].boundingSphere;
}
}
}
static unsafe Hash128 ComputeStateHashDebug(
BoundingSphere[] probeBounds,
HDProbe[] probes,
int probeCount
)
{
#if DEBUG
var result = new Hash128();
if (probeBounds != null)
{
var h = new Hash128();
fixed(BoundingSphere* s = &probeBounds[0])
{
var stride = (ulong)UnsafeUtility.SizeOf<BoundingSphere>();
var size = stride * (ulong)probeBounds.Length;
HashUnsafeUtilities.ComputeHash128(s, size, &h);
}
HashUtilities.AppendHash(ref h, ref result);
}
if (probes != null)
{
var h = new Hash128();
for (int i = 0; i < probes.Length; ++i)
{
if (probes[i] == null || probes[i].Equals(null))
continue;
var instanceID = probes[i].GetInstanceID();
HashUtilities.ComputeHash128(ref instanceID, ref h);
HashUtilities.AppendHash(ref h, ref result);
}
}
return result;
#else
return default;
#endif
}
}
}